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_secinfo.c
21  * @brief GVM management layer: SecInfo
22  *
23  * The SecInfo parts of the GVM management layer.
24  */
25 
26 /**
27  * @brief Enable extra GNU functions.
28  */
29 #define _GNU_SOURCE
30 
31 #include "manage_sql.h"
32 #include "manage_sql_secinfo.h"
33 #include "sql.h"
34 #include "utils.h"
35 
36 #include <assert.h>
37 #include <errno.h>
38 #include <fcntl.h>
39 #include <fnmatch.h>
40 #include <ftw.h>
41 #include <glib/gstdio.h>
42 #include <math.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <sys/file.h>
46 #include <sys/stat.h>
47 #include <sys/types.h>
48 #include <sys/wait.h>
49 #include <unistd.h>
50 
51 #include <gvm/base/proctitle.h>
52 #include <gvm/util/fileutils.h>
53 
54 #undef G_LOG_DOMAIN
55 /**
56  * @brief GLib log domain.
57  */
58 #define G_LOG_DOMAIN "md manage"
59 
60 
61 /* Static variables. */
62 
63 /**
64  * @brief Maximum number of rows in an INSERT.
65  */
66 #define CPE_MAX_CHUNK_SIZE 10000
67 
68 /**
69  * @brief Commit size for updates.
70  */
71 static int secinfo_commit_size = SECINFO_COMMIT_SIZE_DEFAULT;
72 
73 
74 /* Headers. */
75 
76 void
77 manage_db_remove (const gchar *);
78 
79 int
80 manage_db_init (const gchar *);
81 
82 int
83 manage_db_init_indexes (const gchar *);
84 
85 int
86 manage_db_add_constraints (const gchar *);
87 
88 static int
89 sync_cert ();
90 
91 static int
92 update_scap (gboolean);
93 
94 
95 /* Helpers. */
96 
97 /**
98  * @brief Get SQL quoted version of element's text.
99  *
100  * @param[in]  element  Element.
101  *
102  * @return Freshly allocated quoted text.
103  */
104 static gchar *
sql_quote_element_text(element_t element)105 sql_quote_element_text (element_t element)
106 {
107   if (element)
108     {
109       gchar *quoted, *text;
110 
111       text = element_text (element);
112       quoted = sql_quote (text);
113       g_free (text);
114       return quoted;
115     }
116   return g_strdup ("");
117 }
118 
119 /**
120  * @brief Get ISO time from element's text.
121  *
122  * @param[in]  element  Element.
123  *
124  * @return Seconds since epoch.  0 on error.
125  */
126 static int
parse_iso_time_element_text(element_t element)127 parse_iso_time_element_text (element_t element)
128 {
129   if (element)
130     {
131       int ret;
132       gchar *text;
133 
134       text = element_text (element);
135       ret = parse_iso_time (text);
136       g_free (text);
137       return ret;
138     }
139   return 0;
140 }
141 
142 /**
143  * @brief Replace text in a string.
144  *
145  * @param[in]  string  String to replace in.
146  * @param[in]  to      Replacement text.
147  *
148  * @return Freshly allocated string with replacements.
149  */
150 static gchar *
string_replace(const gchar * string,const gchar * to,...)151 string_replace (const gchar *string, const gchar *to, ...)
152 {
153   va_list ap;
154   const gchar *from;
155   gchar *ret;
156 
157   ret = g_strdup (string);
158   va_start (ap, to);
159   while ((from = va_arg (ap, const gchar *)))
160     {
161       gchar **split;
162       split = g_strsplit (ret, from, 0);
163       g_free (ret);
164       ret = g_strjoinv ("~", split);
165       g_strfreev (split);
166     }
167   va_end (ap);
168   return ret;
169 }
170 
171 /**
172  * @brief Increment transaction size, commit and reset at secinfo_commit_size.
173  *
174  * @param[in,out] current_size Pointer to current size to increment and compare.
175  */
176 inline static void
increment_transaction_size(int * current_size)177 increment_transaction_size (int* current_size)
178 {
179   if (secinfo_commit_size && (++(*current_size) > secinfo_commit_size))
180     {
181       *current_size = 0;
182       sql_commit ();
183       sql_begin_immediate ();
184     }
185 }
186 
187 /**
188  * @brief Split a file.
189  *
190  * @param[in]  path  Path to file.
191  * @param[in]  size  Approx size of split files.  In same format that
192  *                   xml_split accepts, eg "200Kb".
193  * @param[in]  tail  Text to replace last line of split files.
194  *
195  * @return Temp dir holding split files.
196  */
197 static const gchar *
split_xml_file(gchar * path,const gchar * size,const gchar * tail)198 split_xml_file (gchar *path, const gchar *size, const gchar *tail)
199 {
200   int ret;
201   static gchar dir[] = "/tmp/gvmd-split-xml-file-XXXXXX";
202   gchar *previous_dir, *command;
203 
204   if (mkdtemp (dir) == NULL)
205     {
206       g_warning ("%s: Failed to make temp dir: %s",
207                  __func__,
208                  strerror (errno));
209       return NULL;
210     }
211 
212   previous_dir = getcwd (NULL, 0);
213   if (previous_dir == NULL)
214     {
215       g_warning ("%s: Failed to getcwd: %s",
216                  __func__,
217                  strerror (errno));
218       return NULL;
219     }
220 
221   if (chdir (dir))
222     {
223       g_warning ("%s: Failed to chdir: %s",
224                  __func__,
225                  strerror (errno));
226       g_free (previous_dir);
227       return NULL;
228     }
229 
230   if (gvm_file_copy (path, "split.xml") == FALSE)
231     {
232       g_free (previous_dir);
233       return NULL;
234     }
235 
236   /* xml_split will chop split.xml into files that are roughly 'size' big.
237    *
238    * The generated files are always put in the directory that holds
239    * split.xml, as follows:
240    *
241    * split.xml      Source XML.
242    * split-00.xml   Master generated XML.  No content, just includes other
243    *                files.  The include statements are wrapped in the
244    *                root element from split.xml.
245    * split-01.xml   Generated XML content.  Wrapped in an <xml_split:root>
246    *                element.
247    * split-02.xml   Second generated content file.
248    * ...
249    * split-112.xml  Last content, for example.
250    *
251    * Parsing the generated files independently will only work if the files
252    * contain the original root element (for example, because the parser
253    * requires the namespace definitions to be present).
254    *
255    * So the command below needs to mess around a little bit to replace the
256    * wrapper XML element in split-01.xml, split-02.xml, etc with the root
257    * element from split-00.xml.
258    *
259    * Using tail and head is not super robust, but it's simple and it will
260    * work as long as xml_split keeps the opening of the wrapper element
261    * in split-00.xml on a dedicated line.  (It doesn't do this for the
262    * closing element, so we use the tail argument instead.)
263    */
264 
265   command = g_strdup_printf
266              ("xml_split -s%s split.xml"
267               " && head -n 2 split-00.xml > head.xml"
268               " && echo '%s' > tail.xml"
269               " && for F in split-*.xml; do"
270               /*   Remove the first two lines and last line. */
271               "    awk 'NR>3 {print last} {last=$0}' $F > body.xml"
272               /*   Combine with new start and end. */
273               "    && cat head.xml body.xml tail.xml > $F;"
274               "    done",
275               size,
276               tail);
277 
278   g_debug ("%s: command: %s", __func__, command);
279   ret = system (command);
280   if ((ret == -1) || WIFEXITED(ret) == 0 || WEXITSTATUS (ret))
281     {
282       g_warning ("%s: system failed with ret %i, %i (%i), %s",
283                  __func__,
284                  ret,
285                  WIFEXITED (ret),
286                  WIFEXITED (ret) ? WEXITSTATUS (ret) : 0,
287                  command);
288       g_free (command);
289 
290       if (chdir (previous_dir))
291         g_warning ("%s: and failed to chdir back", __func__);
292       g_free (previous_dir);
293 
294       return NULL;
295     }
296 
297   g_free (command);
298 
299   if (chdir (previous_dir))
300     g_warning ("%s: Failed to chdir back (will continue anyway)",
301                __func__);
302 
303   g_free (previous_dir);
304 
305   return dir;
306 }
307 
308 
309 /* Helper: buffer structure for INSERTs. */
310 
311 /**
312  * @brief Buffer for INSERT statements.
313  */
314 typedef struct
315 {
316   array_t *statements;     ///< Buffered statements.
317   GString *statement;      ///< Current statement.
318   int current_chunk_size;  ///< Number of rows in current statement.
319   int max_chunk_size;      ///< Max number of rows per INSERT.
320   gchar *open_sql;         ///< SQL to open each statement.
321   gchar *close_sql;        ///< SQL to close each statement.
322 } inserts_t;
323 
324 /**
325  * @brief Check size of current statement.
326  *
327  * @param[in]  inserts         Insert buffer.
328  * @param[in]  max_chunk_size  Max chunk size.
329  * @param[in]  open_sql        SQL to to start each statement.
330  * @param[in]  close_sql       SQL to append to the end of each statement.
331  */
332 static void
inserts_init(inserts_t * inserts,int max_chunk_size,const gchar * open_sql,const gchar * close_sql)333 inserts_init (inserts_t *inserts, int max_chunk_size, const gchar *open_sql,
334               const gchar *close_sql)
335 {
336   inserts->statements = make_array ();
337   inserts->statement = NULL;
338   inserts->current_chunk_size = 0;
339   inserts->max_chunk_size = max_chunk_size;
340   inserts->open_sql = open_sql ? g_strdup (open_sql) : NULL;
341   inserts->close_sql = close_sql ? g_strdup (close_sql) : NULL;
342 }
343 
344 /**
345  * @brief Close the current statement.
346  *
347  * @param[in]  inserts  Insert buffer.
348  */
349 static void
inserts_statement_close(inserts_t * inserts)350 inserts_statement_close (inserts_t *inserts)
351 {
352   if (inserts->statement)
353     {
354       if (inserts->close_sql)
355         g_string_append (inserts->statement, inserts->close_sql);
356       g_string_append (inserts->statement, ";");
357     }
358 }
359 
360 /**
361  * @brief Check size of current statement.
362  *
363  * @param[in]  inserts  Insert buffer.
364  *
365  * @return Whether this is the first value in the statement.
366  */
367 static int
inserts_check_size(inserts_t * inserts)368 inserts_check_size (inserts_t *inserts)
369 {
370   int first;
371 
372   first = 0;
373 
374   if (inserts->statement
375       && inserts->current_chunk_size >= inserts->max_chunk_size)
376     {
377       inserts_statement_close (inserts);
378       array_add (inserts->statements, inserts->statement);
379       inserts->statement = NULL;
380       inserts->current_chunk_size = 0;
381     }
382 
383   if (inserts->statement == NULL)
384     {
385       inserts->statement
386         = g_string_new (inserts->open_sql ? inserts->open_sql : "");
387       first = 1;
388     }
389 
390   return first;
391 }
392 
393 /**
394  * @brief Free everything.
395  *
396  * @param[in]  inserts  Insert buffer.
397  */
398 static void
inserts_free(inserts_t * inserts)399 inserts_free (inserts_t *inserts)
400 {
401   int index;
402 
403   for (index = 0; index < inserts->statements->len; index++)
404     g_string_free (g_ptr_array_index (inserts->statements, index), TRUE);
405   g_ptr_array_free (inserts->statements, TRUE);
406   g_free (inserts->open_sql);
407   g_free (inserts->close_sql);
408   bzero (inserts, sizeof (*inserts));
409 }
410 
411 /**
412  * @brief Run the INSERT SQL, freeing the buffers.
413  *
414  * @param[in]  inserts  Insert buffer.
415  */
416 static void
inserts_run(inserts_t * inserts)417 inserts_run (inserts_t *inserts)
418 {
419   guint index;
420 
421   if (inserts->statement)
422     {
423       inserts_statement_close (inserts);
424       array_add (inserts->statements, inserts->statement);
425       inserts->statement = NULL;
426       inserts->current_chunk_size = 0;
427     }
428 
429   for (index = 0; index < inserts->statements->len; index++)
430     {
431       GString *statement;
432 
433       statement = g_ptr_array_index (inserts->statements, index);
434       sql ("%s", statement->str);
435     }
436 
437   inserts_free (inserts);
438 }
439 
440 
441 /* CPE data. */
442 
443 /**
444  * @brief Gets the SELECT columns for CPE iterators and counts.
445  *
446  * @return The SELECT columns.
447  */
448 static const column_t*
cpe_info_select_columns()449 cpe_info_select_columns ()
450 {
451   static column_t columns[] = CPE_INFO_ITERATOR_COLUMNS;
452   return columns;
453 }
454 
455 /**
456  * @brief Gets the filter columns for CPE iterators and counts.
457  *
458  * @return The filter columns.
459  */
460 static const char **
cpe_info_filter_columns()461 cpe_info_filter_columns ()
462 {
463   static const char *filter_columns[] = CPE_INFO_ITERATOR_FILTER_COLUMNS;
464   return filter_columns;
465 }
466 
467 /**
468  * @brief Count number of cpe.
469  *
470  * @param[in]  get  GET params.
471  *
472  * @return Total number of cpes in filtered set.
473  */
474 int
cpe_info_count(const get_data_t * get)475 cpe_info_count (const get_data_t *get)
476 {
477   static const char *filter_columns[] = CPE_INFO_ITERATOR_FILTER_COLUMNS;
478   static column_t columns[] = CPE_INFO_ITERATOR_COLUMNS;
479   return count ("cpe", get, columns, NULL, filter_columns, 0, 0, 0, FALSE);
480 }
481 
482 /**
483  * @brief Initialise a info iterator.
484  *
485  * @param[in]  iterator        Iterator.
486  * @param[in]  get             GET data.
487  * @param[in]  name            Name of the info
488  *
489  * @return 0 success, 1 failed to find target, 2 failed to find filter,
490  *         -1 error.
491  */
492 int
init_cpe_info_iterator(iterator_t * iterator,get_data_t * get,const char * name)493 init_cpe_info_iterator (iterator_t* iterator, get_data_t *get, const char *name)
494 {
495   static const char *filter_columns[] = CPE_INFO_ITERATOR_FILTER_COLUMNS;
496   static column_t columns[] = CPE_INFO_ITERATOR_COLUMNS;
497   gchar *clause = NULL;
498   int ret;
499 
500   if (get->id)
501     {
502       gchar *quoted = sql_quote (get->id);
503       clause = g_strdup_printf (" AND uuid = '%s'", quoted);
504       g_free (quoted);
505       /* The entry is specified by ID, so filtering just gets in the way. */
506       g_free (get->filter);
507       get->filter = NULL;
508     }
509   else if (name)
510     {
511       gchar *quoted = sql_quote (name);
512       clause = g_strdup_printf (" AND name = '%s'", quoted);
513       g_free (quoted);
514       /* The entry is specified by name, so filtering just gets in the way. */
515       g_free (get->filter);
516       get->filter = NULL;
517     }
518   ret = init_get_iterator (iterator,
519                            "cpe",
520                            get,
521                            columns,
522                            NULL,
523                            filter_columns,
524                            0,
525                            NULL,
526                            clause,
527                            FALSE);
528   g_free (clause);
529   return ret;
530 }
531 
532 /**
533  * @brief Get the title from a CPE iterator.
534  *
535  * @param[in]  iterator  Iterator.
536  *
537  * @return The Title of the CPE, or NULL if iteration is complete.  Freed by
538  *         cleanup_iterator.
539  */
540 DEF_ACCESS (cpe_info_iterator_title, GET_ITERATOR_COLUMN_COUNT);
541 
542 /**
543  * @brief Get the status from a CPE iterator.
544  *
545  * @param[in]  iterator  Iterator.
546  *
547  * @return The Status of the CPE, or NULL if iteration is complete.  Freed by
548  *         cleanup_iterator.
549  */
550 DEF_ACCESS (cpe_info_iterator_status, GET_ITERATOR_COLUMN_COUNT + 1);
551 
552 /**
553  * @brief Get the highest severity Score of all CVE's referencing this cpe.
554  *
555  * @param[in]  iterator  Iterator.
556  *
557  * @return The highest severity score of the CPE,
558  *         or NULL if iteration is complete. Freed by cleanup_iterator.
559  */
560 DEF_ACCESS (cpe_info_iterator_severity, GET_ITERATOR_COLUMN_COUNT + 3);
561 
562 /**
563  * @brief Get the Number of CVE's referencing this cpe from a CPE iterator.
564  *
565  * @param[in]  iterator  Iterator.
566  *
567  * @return The Number of references to the CPE, or NULL if iteration is
568  *         complete. Freed by cleanup_iterator.
569  */
570 DEF_ACCESS (cpe_info_iterator_cve_refs, GET_ITERATOR_COLUMN_COUNT + 4);
571 
572 /**
573  * @brief Get the NVD ID for this CPE.
574  *
575  * @param[in]  iterator  Iterator.
576  *
577  * @return The NVD ID of this CPE, or NULL if iteration is
578  *         complete. Freed by cleanup_iterator.
579  */
580 DEF_ACCESS (cpe_info_iterator_nvd_id, GET_ITERATOR_COLUMN_COUNT + 5);
581 
582 
583 /* CVE data. */
584 
585 /**
586  * @brief Gets the SELECT columns for CVE iterators and counts.
587  *
588  * @return The SELECT columns.
589  */
590 static const column_t*
cve_info_select_columns()591 cve_info_select_columns ()
592 {
593   static column_t columns[] = CVE_INFO_ITERATOR_COLUMNS;
594   return columns;
595 }
596 
597 /**
598  * @brief Gets the filter columns for CVE iterators and counts.
599  *
600  * @return The filter columns.
601  */
602 static const char **
cve_info_filter_columns()603 cve_info_filter_columns ()
604 {
605   static const char *filter_columns[] = CVE_INFO_ITERATOR_FILTER_COLUMNS;
606   return filter_columns;
607 }
608 
609 /**
610  * @brief Initialise an CVE iterator, for CVEs reported for a certain CPE.
611  *
612  * @param[in]  iterator    Iterator.
613  * @param[in]  cve         CVE.
614  * @param[in]  ascending   Whether to sort ascending or descending.
615  * @param[in]  sort_field  Field to sort on, or NULL for "id".
616  */
617 void
init_cpe_cve_iterator(iterator_t * iterator,const char * cve,int ascending,const char * sort_field)618 init_cpe_cve_iterator (iterator_t *iterator, const char *cve, int ascending,
619                        const char *sort_field)
620 {
621   gchar *quoted_cpe;
622   assert (cve);
623   quoted_cpe = sql_quote (cve);
624   init_iterator (iterator,
625                  "SELECT id, name, severity FROM cves"
626                  " WHERE id IN"
627                  " (SELECT cve FROM affected_products"
628                  "  WHERE cpe ="
629                  "  (SELECT id FROM cpes WHERE name = '%s'))"
630                  " ORDER BY %s %s;",
631                  quoted_cpe,
632                  sort_field ? sort_field : "severity DESC, name",
633                  ascending ? "ASC" : "DESC");
634   g_free (quoted_cpe);
635 }
636 
637 /**
638  * @brief Get the name from a CVE iterator.
639  *
640  * @param[in]  iterator  Iterator.
641  *
642  * @return The name of the CVE, or NULL if iteration is complete.  Freed by
643  *         cleanup_iterator.
644  */
645 DEF_ACCESS (cve_iterator_name, 1);
646 
647 /**
648  * @brief Get the severity score from a CVE iterator.
649  *
650  * @param[in]  iterator  Iterator.
651  *
652  * @return The CVSS score of the CVE,
653  *         or NULL if iteration is complete.  Freed by cleanup_iterator.
654  */
655 DEF_ACCESS (cve_iterator_cvss_score, 2);
656 
657 /**
658  * @brief Get the CVSS score for a CVE.
659  *
660  * @param[in]  cve  CVE-ID of the CVE to get the score of.
661  *
662  * @return The CVSS score of the CVE.
663  */
664 gchar *
cve_cvss_base(const gchar * cve)665 cve_cvss_base (const gchar *cve)
666 {
667   gchar *quoted_cve, *ret;
668   quoted_cve = sql_quote (cve);
669   ret = sql_string ("SELECT severity FROM cves WHERE name = '%s'",
670                     quoted_cve);
671   g_free (quoted_cve);
672   return ret;
673 }
674 
675 /**
676  * @brief Count number of cve.
677  *
678  * @param[in]  get  GET params.
679  *
680  * @return Total number of cpes in filtered set.
681  */
682 int
cve_info_count(const get_data_t * get)683 cve_info_count (const get_data_t *get)
684 {
685   static const char *filter_columns[] = CVE_INFO_ITERATOR_FILTER_COLUMNS;
686   static column_t columns[] = CVE_INFO_ITERATOR_COLUMNS;
687   return count ("cve", get, columns, NULL, filter_columns, 0, 0, 0, FALSE);
688 }
689 
690 /**
691  * @brief Initialise a info iterator.
692  *
693  * @param[in]  iterator        Iterator.
694  * @param[in]  get             GET data.
695  * @param[in]  name            Name of the info
696  *
697  * @return 0 success, 1 failed to find target, 2 failed to find filter,
698  *         -1 error.
699  */
700 int
init_cve_info_iterator(iterator_t * iterator,get_data_t * get,const char * name)701 init_cve_info_iterator (iterator_t* iterator, get_data_t *get, const char *name)
702 {
703   static const char *filter_columns[] = CVE_INFO_ITERATOR_FILTER_COLUMNS;
704   static column_t columns[] = CVE_INFO_ITERATOR_COLUMNS;
705   gchar *clause = NULL;
706   int ret;
707 
708   if (get->id)
709     {
710       gchar *quoted = sql_quote (get->id);
711       clause = g_strdup_printf (" AND uuid = '%s'", quoted);
712       g_free (quoted);
713       /* The entry is specified by ID, so filtering just gets in the way. */
714       g_free (get->filter);
715       get->filter = NULL;
716     }
717   else if (name)
718     {
719       gchar *quoted = sql_quote (name);
720       clause = g_strdup_printf (" AND name = '%s'", quoted);
721       g_free (quoted);
722       /* The entry is specified by name, so filtering just gets in the way. */
723       g_free (get->filter);
724       get->filter = NULL;
725     }
726   ret = init_get_iterator (iterator,
727                            "cve",
728                            get,
729                            columns,
730                            NULL,
731                            filter_columns,
732                            0,
733                            NULL,
734                            clause,
735                            FALSE);
736   g_free (clause);
737   return ret;
738 }
739 
740 /**
741  * @brief Get the CVSS attack vector for this CVE.
742  *
743  * @param[in]  iterator  Iterator.
744  *
745  * @return The CVSS attack vector of this CVE, or NULL if iteration is
746  *         complete. Freed by cleanup_iterator.
747  */
748 DEF_ACCESS (cve_info_iterator_vector, GET_ITERATOR_COLUMN_COUNT);
749 
750 /**
751  * @brief Get the CVSS attack complexity for this CVE.
752  *
753  * @param[in]  iterator  Iterator.
754  *
755  * @return The CVSS attack complexity of this CVE, or NULL if iteration is
756  *         complete. Freed by cleanup_iterator.
757  */
758 DEF_ACCESS (cve_info_iterator_complexity, GET_ITERATOR_COLUMN_COUNT + 1);
759 
760 /**
761  * @brief Get a space separated list of CPEs affected by this CVE.
762  *
763  * @param[in]  iterator  Iterator.
764  *
765  * @return A space separated list of CPEs or NULL if iteration is
766  *         complete. Freed by cleanup_iterator.
767  */
768 DEF_ACCESS (cve_info_iterator_products, GET_ITERATOR_COLUMN_COUNT + 1);
769 
770 /**
771  * @brief Get the severity score for this CVE.
772  *
773  * @param[in]  iterator  Iterator.
774  *
775  * @return The severity score of this CVE, or NULL if iteration is complete.
776  *         Freed by cleanup_iterator.
777  */
778 DEF_ACCESS (cve_info_iterator_severity, GET_ITERATOR_COLUMN_COUNT + 2);
779 
780 /**
781  * @brief Get the Summary for this CVE.
782  *
783  * @param[in]  iterator  Iterator.
784  *
785  * @return The Summary of this CVE, or NULL if iteration is
786  *         complete. Freed by cleanup_iterator.
787  */
788 DEF_ACCESS (cve_info_iterator_description, GET_ITERATOR_COLUMN_COUNT + 3);
789 
790 
791 /* OVAL data. */
792 
793 /**
794  * @brief Gets the SELECT columns for OVAL definition iterators and counts.
795  *
796  * @return The SELECT columns.
797  */
798 static const column_t*
ovaldef_info_select_columns()799 ovaldef_info_select_columns ()
800 {
801   static column_t columns[] = OVALDEF_INFO_ITERATOR_COLUMNS;
802   return columns;
803 }
804 
805 /**
806  * @brief Gets the filter columns for OVAL definition iterators and counts.
807  *
808  * @return The filter columns.
809  */
810 static const char **
ovaldef_info_filter_columns()811 ovaldef_info_filter_columns ()
812 {
813   static const char *filter_columns[] = OVALDEF_INFO_ITERATOR_FILTER_COLUMNS;
814   return filter_columns;
815 }
816 
817 /**
818  * @brief Initialise an OVAL definition (ovaldef) info iterator.
819  *
820  * @param[in]  iterator        Iterator.
821  * @param[in]  get             GET data.
822  * @param[in]  name            Name of the info
823  *
824  * @return 0 success, 1 failed to find target, 2 failed to find filter,
825  *         -1 error.
826  */
827 int
init_ovaldef_info_iterator(iterator_t * iterator,get_data_t * get,const char * name)828 init_ovaldef_info_iterator (iterator_t* iterator, get_data_t *get,
829                             const char *name)
830 {
831   static const char *filter_columns[] = OVALDEF_INFO_ITERATOR_FILTER_COLUMNS;
832   static column_t columns[] = OVALDEF_INFO_ITERATOR_COLUMNS;
833   gchar *clause = NULL;
834   int ret;
835 
836   if (get->id)
837     {
838       gchar *quoted = sql_quote (get->id);
839       clause = g_strdup_printf (" AND uuid = '%s'", quoted);
840       g_free (quoted);
841       /* The entry is specified by ID, so filtering just gets in the way. */
842       g_free (get->filter);
843       get->filter = NULL;
844     }
845   else if (name)
846     {
847       gchar *quoted = sql_quote (name);
848       clause = g_strdup_printf (" AND name = '%s'", quoted);
849       g_free (quoted);
850       /* The entry is specified by name, so filtering just gets in the way. */
851       g_free (get->filter);
852       get->filter = NULL;
853     }
854   ret = init_get_iterator (iterator,
855                            "ovaldef",
856                            get,
857                            columns,
858                            NULL,
859                            filter_columns,
860                            0,
861                            NULL,
862                            clause,
863                            FALSE);
864   g_free (clause);
865   return ret;
866 }
867 
868 /**
869  * @brief Count number of ovaldef.
870  *
871  * @param[in]  get  GET params.
872  *
873  * @return Total number of OVAL definitions in filtered set.
874  */
875 int
ovaldef_info_count(const get_data_t * get)876 ovaldef_info_count (const get_data_t *get)
877 {
878   static const char *filter_columns[] = OVALDEF_INFO_ITERATOR_FILTER_COLUMNS;
879   static column_t columns[] = OVALDEF_INFO_ITERATOR_COLUMNS;
880   return count ("ovaldef", get, columns, NULL, filter_columns, 0, 0, 0, FALSE);
881 }
882 
883 /**
884  * @brief Get the version number from an OVALDEF iterator.
885  *
886  * @param[in]  iterator  Iterator.
887  *
888  * @return The version number of the OVAL definition,
889  *         or NULL if iteration is complete.
890  *         Freed by cleanup_iterator.
891  */
892 DEF_ACCESS (ovaldef_info_iterator_version, GET_ITERATOR_COLUMN_COUNT);
893 
894 /**
895  * @brief Get the deprecation status from an OVALDEF iterator.
896  *
897  * @param[in]  iterator  Iterator.
898  *
899  * @return True if the OVAL definition is deprecated, false if not,
900  *         or NULL if iteration is complete.
901  *         Freed by cleanup_iterator.
902  */
903 DEF_ACCESS (ovaldef_info_iterator_deprecated, GET_ITERATOR_COLUMN_COUNT + 1);
904 
905 /**
906  * @brief Get the definition class from an OVALDEF iterator.
907  *
908  * @param[in]  iterator  Iterator.
909  *
910  * @return The definition class (e.g. 'patch' or 'vulnerability') of the OVAL
911  *         definition, or NULL if iteration is complete.
912  *         Freed by cleanup_iterator.
913  */
914 DEF_ACCESS (ovaldef_info_iterator_class, GET_ITERATOR_COLUMN_COUNT + 2);
915 
916 /**
917  * @brief Get the title from an OVALDEF iterator.
918  *
919  * @param[in]  iterator  Iterator.
920  *
921  * @return The title / short description of the OVAL definition,
922  *         or NULL if iteration is complete.
923  *         Freed by cleanup_iterator.
924  */
925 DEF_ACCESS (ovaldef_info_iterator_title, GET_ITERATOR_COLUMN_COUNT + 3);
926 
927 /**
928  * @brief Get the description from an OVALDEF iterator.
929  *
930  * @param[in]  iterator  Iterator.
931  *
932  * @return The long description of the OVAL definition,
933  *         or NULL if iteration is complete.
934  *         Freed by cleanup_iterator.
935  */
936 DEF_ACCESS (ovaldef_info_iterator_description, GET_ITERATOR_COLUMN_COUNT + 4);
937 
938 /**
939  * @brief Get the source xml file from an OVALDEF iterator.
940  *
941  * @param[in]  iterator  Iterator.
942  *
943  * @return The short xml source file name of the OVAL definition,
944  *         or NULL if iteration is complete.
945  *         Freed by cleanup_iterator.
946  */
947 DEF_ACCESS (ovaldef_info_iterator_file, GET_ITERATOR_COLUMN_COUNT + 5);
948 
949 /**
950  * @brief Get the repository entry status from an OVALDEF iterator.
951  *
952  * @param[in]  iterator  Iterator.
953  *
954  * @return The repository entry status of the OVAL definition,
955  *         or NULL if iteration is complete.
956  *         Freed by cleanup_iterator.
957  */
958 DEF_ACCESS (ovaldef_info_iterator_status, GET_ITERATOR_COLUMN_COUNT + 6);
959 
960 /**
961  * @brief Get maximum severity score from an OVALDEF iterator.
962  *
963  * @param[in]  iterator  Iterator.
964  *
965  * @return The maximum severity score of the OVAL definition,
966  *         or NULL if iteration is complete.
967  *         Freed by cleanup_iterator.
968  */
969 DEF_ACCESS (ovaldef_info_iterator_severity, GET_ITERATOR_COLUMN_COUNT + 7);
970 
971 /**
972  * @brief Get number of referenced CVEs from an OVALDEF iterator.
973  *
974  * @param[in]  iterator  Iterator.
975  *
976  * @return The number of CVEs referenced CVEs of the OVAL definition,
977  *         or NULL if iteration is complete.
978  *         Freed by cleanup_iterator.
979  */
980 DEF_ACCESS (ovaldef_info_iterator_cve_refs, GET_ITERATOR_COLUMN_COUNT + 8);
981 
982 
983 /**
984  * @brief Get the short file name for an OVALDEF.
985  *
986  * @param[in]  item_id  Full OVAL identifier with file suffix.
987  *
988  * @return The file name of the OVAL definition relative to the SCAP directory,
989  *         Freed by g_free.
990  */
991 gchar*
get_ovaldef_short_filename(char * item_id)992 get_ovaldef_short_filename (char* item_id)
993 {
994   return sql_string ("SELECT xml_file FROM ovaldefs WHERE uuid = '%s';",
995                      item_id);
996 }
997 
998 /**
999  * @brief Get the uuid for an OVALDEF from a name and file name.
1000  *
1001  * @param[in]  name     Oval definition name.
1002  * @param[in]  fname    Oval definition file name.
1003  *
1004  * @return The OVAL definition uuid from the SCAP directory. Freed by g_free.
1005  */
1006 char*
ovaldef_uuid(const char * name,const char * fname)1007 ovaldef_uuid (const char *name, const char *fname)
1008 {
1009   char *quoted_name, *quoted_fname, *ret;
1010 
1011   assert (name);
1012   assert (fname);
1013   quoted_name = sql_quote (name);
1014   quoted_fname = sql_quote (fname);
1015   ret = sql_string ("SELECT uuid FROM ovaldefs WHERE name = '%s'"
1016                     " AND xml_file = '%s';", name, fname);
1017   g_free (quoted_name);
1018   g_free (quoted_fname);
1019   return ret;
1020 }
1021 
1022 /**
1023  * @brief Get the severity of an OVALDEF using an ID.
1024  *
1025  * @param[in]  id  Oval definition ID.
1026  *
1027  * @return The severity of the OVAL definition from the SCAP directory.
1028  *         Freed by g_free.
1029  */
1030 char *
ovaldef_severity(const char * id)1031 ovaldef_severity (const char *id)
1032 {
1033   char *quoted_id, *ret;
1034 
1035   assert (id);
1036   quoted_id = sql_quote (id);
1037   ret = sql_string ("SELECT severity FROM ovaldefs WHERE uuid = '%s';",
1038                     quoted_id);
1039   g_free (quoted_id);
1040   return ret;
1041 }
1042 
1043 /**
1044  * @brief Get the version of an OVALDEF using an ID.
1045  *
1046  * @param[in]  id  Oval definition ID.
1047  *
1048  * @return The version of the OVAL definition from the SCAP directory.
1049  *         Freed by g_free.
1050  */
1051 char *
ovaldef_version(const char * id)1052 ovaldef_version (const char *id)
1053 {
1054   char *quoted_id, *ret;
1055 
1056   assert (id);
1057   quoted_id = sql_quote (id);
1058   ret = sql_string ("SELECT version FROM ovaldefs WHERE uuid = '%s';",
1059                     quoted_id);
1060   g_free (quoted_id);
1061   return ret;
1062 }
1063 
1064 /**
1065  * @brief Get the CVE names of an OVALDEF as ", " separated str.
1066  *
1067  * @param[in]  id  Oval definition ID.
1068  *
1069  * @return String of CVEs affecting of the OVAL definition, NULL otherwise.
1070  *         Freed by g_free.
1071  */
1072 char *
ovaldef_cves(const char * id)1073 ovaldef_cves (const char *id)
1074 {
1075   char *quoted_id, *ret = NULL;
1076   iterator_t iterator;
1077 
1078   assert (id);
1079   quoted_id = sql_quote (id);
1080   init_iterator (&iterator,
1081                  "SELECT DISTINCT cves.name FROM cves, ovaldefs,"
1082                  " affected_ovaldefs WHERE ovaldefs.uuid = '%s'"
1083                  " AND cves.id = affected_ovaldefs.cve"
1084                  " AND ovaldefs.id = affected_ovaldefs.ovaldef;", quoted_id);
1085   g_free (quoted_id);
1086   while (next (&iterator))
1087     {
1088       char *tmp = ret;
1089       ret = g_strdup_printf ("%s%s%s", ret ?: "", ret ? ", " : "",
1090                              iterator_string (&iterator, 0));
1091       g_free (tmp);
1092     }
1093   cleanup_iterator (&iterator);
1094   return ret;
1095 }
1096 
1097 
1098 /* CERT-Bund data. */
1099 
1100 /**
1101  * @brief Gets the SELECT columns for CERT-Bund advisory iterators and counts.
1102  *
1103  * @return The SELECT columns.
1104  */
1105 static const column_t*
cert_bund_adv_info_select_columns()1106 cert_bund_adv_info_select_columns ()
1107 {
1108   static column_t columns[] = CERT_BUND_ADV_INFO_ITERATOR_COLUMNS;
1109   return columns;
1110 }
1111 
1112 /**
1113  * @brief Gets the filter columns for CERT-Bund advisory iterators and counts.
1114  *
1115  * @return The filter columns.
1116  */
1117 static const char **
cert_bund_adv_info_filter_columns()1118 cert_bund_adv_info_filter_columns ()
1119 {
1120   static const char *filter_columns[]
1121     = CERT_BUND_ADV_INFO_ITERATOR_FILTER_COLUMNS;
1122   return filter_columns;
1123 }
1124 
1125 /**
1126  * @brief Initialise an CERT-Bund advisory (cert_bund_adv) info iterator.
1127  *
1128  * @param[in]  iterator        Iterator.
1129  * @param[in]  get             GET data.
1130  * @param[in]  name            Name of the info
1131  *
1132  * @return 0 success, 1 failed to find target, 2 failed to find filter,
1133  *         -1 error.
1134  */
1135 int
init_cert_bund_adv_info_iterator(iterator_t * iterator,get_data_t * get,const char * name)1136 init_cert_bund_adv_info_iterator (iterator_t* iterator, get_data_t *get,
1137                                   const char *name)
1138 {
1139   static const char *filter_columns[] =
1140       CERT_BUND_ADV_INFO_ITERATOR_FILTER_COLUMNS;
1141   static column_t columns[] = CERT_BUND_ADV_INFO_ITERATOR_COLUMNS;
1142   gchar *clause = NULL;
1143   int ret;
1144 
1145   if (get->id)
1146     {
1147       gchar *quoted = sql_quote (get->id);
1148       clause = g_strdup_printf (" AND uuid = '%s'", quoted);
1149       g_free (quoted);
1150       /* The entry is specified by ID, so filtering just gets in the way. */
1151       g_free (get->filter);
1152       get->filter = NULL;
1153     }
1154   else if (name)
1155     {
1156       gchar *quoted = sql_quote (name);
1157       clause = g_strdup_printf (" AND name = '%s'", quoted);
1158       g_free (quoted);
1159       /* The entry is specified by name, so filtering just gets in the way. */
1160       g_free (get->filter);
1161       get->filter = NULL;
1162     }
1163   ret = init_get_iterator (iterator,
1164                            "cert_bund_adv",
1165                            get,
1166                            columns,
1167                            NULL,
1168                            filter_columns,
1169                            0,
1170                            NULL,
1171                            clause,
1172                            FALSE);
1173   g_free (clause);
1174   return ret;
1175 }
1176 
1177 /**
1178  * @brief Count number of cert_bund_adv.
1179  *
1180  * @param[in]  get  GET params.
1181  *
1182  * @return Total number of CERT-Bund advisories in filtered set.
1183  */
1184 int
cert_bund_adv_info_count(const get_data_t * get)1185 cert_bund_adv_info_count (const get_data_t *get)
1186 {
1187   static const char *filter_columns[] =
1188                       CERT_BUND_ADV_INFO_ITERATOR_FILTER_COLUMNS;
1189   static column_t columns[] = CERT_BUND_ADV_INFO_ITERATOR_COLUMNS;
1190   return count ("cert_bund_adv", get, columns, NULL, filter_columns,
1191                 0, 0, 0, FALSE);
1192 }
1193 
1194 /**
1195  * @brief Get the title from an CERT_BUND_ADV iterator.
1196  *
1197  * @param[in]  iterator  Iterator.
1198  *
1199  * @return The title of the CERT-Bund advisory,
1200  *         or NULL if iteration is complete.
1201  *         Freed by cleanup_iterator.
1202  */
1203 DEF_ACCESS (cert_bund_adv_info_iterator_title,
1204             GET_ITERATOR_COLUMN_COUNT);
1205 
1206 /**
1207  * @brief Get the summary from an CERT_BUND_ADV iterator.
1208  *
1209  * @param[in]  iterator  Iterator.
1210  *
1211  * @return The summary of the CERT-Bund advisory,
1212  *         or NULL if iteration is complete.
1213  *         Freed by cleanup_iterator.
1214  */
1215 DEF_ACCESS (cert_bund_adv_info_iterator_summary,
1216             GET_ITERATOR_COLUMN_COUNT + 1);
1217 
1218 /**
1219  * @brief Get the number of cves from an CERT_BUND_ADV iterator.
1220  *
1221  * @param[in]  iterator  Iterator.
1222  *
1223  * @return The number of CVEs referenced in the CERT-Bund advisory,
1224  *         or NULL if iteration is complete.
1225  *         Freed by cleanup_iterator.
1226  */
1227 DEF_ACCESS (cert_bund_adv_info_iterator_cve_refs,
1228             GET_ITERATOR_COLUMN_COUNT + 2);
1229 
1230 /**
1231  * @brief Get the maximum severity score from an CERT_BUND_ADV iterator.
1232  *
1233  * @param[in]  iterator  Iterator.
1234  *
1235  * @return The maximum severity score of the CVEs referenced
1236  *         in the CERT-Bund advisory, or NULL if iteration is complete.
1237  *         Freed by cleanup_iterator.
1238  */
1239 DEF_ACCESS (cert_bund_adv_info_iterator_severity,
1240             GET_ITERATOR_COLUMN_COUNT + 3);
1241 
1242 /**
1243  * @brief Initialise CVE iterator, for CVEs referenced by a CERT-Bund advisory.
1244  *
1245  * @param[in]  iterator    Iterator.
1246  * @param[in]  cve         Name of the CVE.
1247  * @param[in]  ascending   Whether to sort ascending or descending.
1248  * @param[in]  sort_field  Field to sort on, or NULL for "id".
1249  */
1250 void
init_cve_cert_bund_adv_iterator(iterator_t * iterator,const char * cve,int ascending,const char * sort_field)1251 init_cve_cert_bund_adv_iterator (iterator_t *iterator, const char *cve,
1252                                 int ascending, const char *sort_field)
1253 {
1254   static column_t select_columns[] = CERT_BUND_ADV_INFO_ITERATOR_COLUMNS;
1255   gchar *columns;
1256 
1257   assert (cve);
1258 
1259   columns = columns_build_select (select_columns);
1260   init_iterator (iterator,
1261                  "SELECT %s"
1262                  " FROM cert_bund_advs"
1263                  " WHERE id IN (SELECT adv_id FROM cert_bund_cves"
1264                  "              WHERE cve_name = '%s')"
1265                  " ORDER BY %s %s;",
1266                  columns,
1267                  cve,
1268                  sort_field ? sort_field : "name",
1269                  ascending ? "ASC" : "DESC");
1270   g_free (columns);
1271 }
1272 
1273 /**
1274  * @brief Initialise an CERT-Bund iterator, for advisories relevant to a NVT.
1275  *
1276  * @param[in]  iterator    Iterator.
1277  * @param[in]  oid         OID of the NVT.
1278  */
1279 void
init_nvt_cert_bund_adv_iterator(iterator_t * iterator,const char * oid)1280 init_nvt_cert_bund_adv_iterator (iterator_t *iterator, const char *oid)
1281 {
1282   assert (oid);
1283 
1284   init_iterator (iterator,
1285                  "SELECT name"
1286                  " FROM cert_bund_advs"
1287                  " WHERE id IN (SELECT adv_id FROM cert_bund_cves"
1288                  "              WHERE cve_name IN (SELECT ref_id"
1289                  "                                 FROM vt_refs"
1290                  "                                 WHERE vt_oid = '%s'"
1291 		 "                                   AND type = 'cve'))"
1292                  " ORDER BY name DESC;",
1293                  oid);
1294 }
1295 
1296 /**
1297  * @brief Get a column value from an iterator.
1298  *
1299  * @param[in]  iterator  Iterator.
1300  *
1301  * @return Value of the column or NULL if iteration is complete.
1302  */
1303 DEF_ACCESS (nvt_cert_bund_adv_iterator_name, 0);
1304 
1305 
1306 /* DFN-CERT data. */
1307 
1308 /**
1309  * @brief Gets the SELECT columns for DFN-CERT advisory iterators and counts.
1310  *
1311  * @return The SELECT columns.
1312  */
1313 static const column_t*
dfn_cert_adv_info_select_columns()1314 dfn_cert_adv_info_select_columns ()
1315 {
1316   static column_t columns[] = DFN_CERT_ADV_INFO_ITERATOR_COLUMNS;
1317   return columns;
1318 }
1319 
1320 /**
1321  * @brief Gets the filter columns for DFN-CERT advisory iterators and counts.
1322  *
1323  * @return The filter columns.
1324  */
1325 static const char **
dfn_cert_adv_info_filter_columns()1326 dfn_cert_adv_info_filter_columns ()
1327 {
1328   static const char *filter_columns[]
1329     = DFN_CERT_ADV_INFO_ITERATOR_FILTER_COLUMNS;
1330   return filter_columns;
1331 }
1332 
1333 /**
1334  * @brief Initialise an DFN-CERT advisory (dfn_cert_adv) info iterator.
1335  *
1336  * @param[in]  iterator        Iterator.
1337  * @param[in]  get             GET data.
1338  * @param[in]  name            Name of the info
1339  *
1340  * @return 0 success, 1 failed to find target, 2 failed to find filter,
1341  *         -1 error.
1342  */
1343 int
init_dfn_cert_adv_info_iterator(iterator_t * iterator,get_data_t * get,const char * name)1344 init_dfn_cert_adv_info_iterator (iterator_t* iterator, get_data_t *get,
1345                             const char *name)
1346 {
1347   static const char *filter_columns[] =
1348       DFN_CERT_ADV_INFO_ITERATOR_FILTER_COLUMNS;
1349   static column_t columns[] = DFN_CERT_ADV_INFO_ITERATOR_COLUMNS;
1350   gchar *clause = NULL;
1351   int ret;
1352 
1353   if (get->id)
1354     {
1355       gchar *quoted = sql_quote (get->id);
1356       clause = g_strdup_printf (" AND uuid = '%s'", quoted);
1357       g_free (quoted);
1358       /* The entry is specified by ID, so filtering just gets in the way. */
1359       g_free (get->filter);
1360       get->filter = NULL;
1361     }
1362   else if (name)
1363     {
1364       gchar *quoted = sql_quote (name);
1365       clause = g_strdup_printf (" AND name = '%s'", quoted);
1366       g_free (quoted);
1367       /* The entry is specified by name, so filtering just gets in the way. */
1368       g_free (get->filter);
1369       get->filter = NULL;
1370     }
1371   ret = init_get_iterator (iterator,
1372                            "dfn_cert_adv",
1373                            get,
1374                            columns,
1375                            NULL,
1376                            filter_columns,
1377                            0,
1378                            NULL,
1379                            clause,
1380                            FALSE);
1381   g_free (clause);
1382   return ret;
1383 }
1384 
1385 /**
1386  * @brief Count number of dfn_cert_adv.
1387  *
1388  * @param[in]  get  GET params.
1389  *
1390  * @return Total number of DFN-CERT advisories in filtered set.
1391  */
1392 int
dfn_cert_adv_info_count(const get_data_t * get)1393 dfn_cert_adv_info_count (const get_data_t *get)
1394 {
1395   static const char *filter_columns[] =
1396                       DFN_CERT_ADV_INFO_ITERATOR_FILTER_COLUMNS;
1397   static column_t columns[] = DFN_CERT_ADV_INFO_ITERATOR_COLUMNS;
1398   return count ("dfn_cert_adv", get, columns, NULL, filter_columns,
1399                 0, 0, 0, FALSE);
1400 }
1401 
1402 /**
1403  * @brief Get the title from an DFN_CERT_ADV iterator.
1404  *
1405  * @param[in]  iterator  Iterator.
1406  *
1407  * @return The title of the DFN-CERT advisory,
1408  *         or NULL if iteration is complete.
1409  *         Freed by cleanup_iterator.
1410  */
1411 DEF_ACCESS (dfn_cert_adv_info_iterator_title, GET_ITERATOR_COLUMN_COUNT);
1412 
1413 /**
1414  * @brief Get the summary from an DFN_CERT_ADV iterator.
1415  *
1416  * @param[in]  iterator  Iterator.
1417  *
1418  * @return The summary of the DFN-CERT advisory,
1419  *         or NULL if iteration is complete.
1420  *         Freed by cleanup_iterator.
1421  */
1422 DEF_ACCESS (dfn_cert_adv_info_iterator_summary, GET_ITERATOR_COLUMN_COUNT + 1);
1423 
1424 /**
1425  * @brief Get the number of cves from an DFN_CERT_ADV iterator.
1426  *
1427  * @param[in]  iterator  Iterator.
1428  *
1429  * @return The number of CVEs referenced in the DFN-CERT advisory,
1430  *         or NULL if iteration is complete.
1431  *         Freed by cleanup_iterator.
1432  */
1433 DEF_ACCESS (dfn_cert_adv_info_iterator_cve_refs, GET_ITERATOR_COLUMN_COUNT + 2);
1434 
1435 /**
1436  * @brief Get the maximum severity score from an DFN_CERT_ADV iterator.
1437  *
1438  * @param[in]  iterator  Iterator.
1439  *
1440  * @return The maximum score of the CVEs referenced
1441  *         in the DFN-CERT advisory, or NULL if iteration is complete.
1442  *         Freed by cleanup_iterator.
1443  */
1444 DEF_ACCESS (dfn_cert_adv_info_iterator_severity, GET_ITERATOR_COLUMN_COUNT + 3);
1445 
1446 /**
1447  * @brief Initialise CVE iterator, for CVEs referenced by a DFN-CERT advisory.
1448  *
1449  * @param[in]  iterator    Iterator.
1450  * @param[in]  cve         Name of the CVE.
1451  * @param[in]  ascending   Whether to sort ascending or descending.
1452  * @param[in]  sort_field  Field to sort on, or NULL for "id".
1453  */
1454 void
init_cve_dfn_cert_adv_iterator(iterator_t * iterator,const char * cve,int ascending,const char * sort_field)1455 init_cve_dfn_cert_adv_iterator (iterator_t *iterator, const char *cve,
1456                                 int ascending, const char *sort_field)
1457 {
1458   static column_t select_columns[] = DFN_CERT_ADV_INFO_ITERATOR_COLUMNS;
1459   gchar *columns;
1460 
1461   assert (cve);
1462 
1463   columns = columns_build_select (select_columns);
1464   init_iterator (iterator,
1465                  "SELECT %s"
1466                  " FROM dfn_cert_advs"
1467                  " WHERE id IN (SELECT adv_id FROM dfn_cert_cves"
1468                  "              WHERE cve_name = '%s')"
1469                  " ORDER BY %s %s;",
1470                  columns,
1471                  cve,
1472                  sort_field ? sort_field : "name",
1473                  ascending ? "ASC" : "DESC");
1474   g_free (columns);
1475 }
1476 
1477 /**
1478  * @brief Initialise an DFN-CERT iterator, for advisories relevant to a NVT.
1479  *
1480  * @param[in]  iterator    Iterator.
1481  * @param[in]  oid         OID of the NVT.
1482  */
1483 void
init_nvt_dfn_cert_adv_iterator(iterator_t * iterator,const char * oid)1484 init_nvt_dfn_cert_adv_iterator (iterator_t *iterator, const char *oid)
1485 {
1486   assert (oid);
1487 
1488   init_iterator (iterator,
1489                  "SELECT name"
1490                  " FROM dfn_cert_advs"
1491                  " WHERE id IN (SELECT adv_id FROM dfn_cert_cves"
1492                  "              WHERE cve_name IN (SELECT ref_id"
1493                  "                                 FROM vt_refs"
1494                  "                                 WHERE vt_oid = '%s'"
1495 		 "                                   AND type = 'cve'))"
1496                  " ORDER BY name DESC;",
1497                  oid);
1498 }
1499 
1500 /**
1501  * @brief Get a column value from an iterator.
1502  *
1503  * @param[in]  iterator  Iterator.
1504  *
1505  * @return Value of the column or NULL if iteration is complete.
1506  */
1507 DEF_ACCESS (nvt_dfn_cert_adv_iterator_name, 0);
1508 
1509 
1510 /* All SecInfo data. */
1511 
1512 /**
1513  * @brief Count number of SecInfo items created or modified after a given time.
1514  *
1515  * @param[in]  get            GET params.
1516  * @param[in]  type           The type of SecInfo to count.
1517  * @param[in]  count_time     Time SecInfo must be created or modified after.
1518  * @param[in]  get_modified   Whether to get the modification time.
1519  *
1520  * @return Total number of items in filtered set.
1521  */
1522 int
secinfo_count_after(const get_data_t * get,const char * type,time_t count_time,gboolean get_modified)1523 secinfo_count_after (const get_data_t *get,
1524                      const char *type,
1525                      time_t count_time,
1526                      gboolean get_modified)
1527 {
1528   const char **filter_columns;
1529   const column_t *columns;
1530   gchar *extra_where;
1531   int ret;
1532 
1533   if (strcmp (type, "cpe") == 0)
1534     {
1535       columns = cpe_info_select_columns ();
1536       filter_columns = cpe_info_filter_columns ();
1537     }
1538   else if (strcmp (type, "cve") == 0)
1539     {
1540       columns = cve_info_select_columns ();
1541       filter_columns = cve_info_filter_columns ();
1542     }
1543   else if (strcmp (type, "ovaldef") == 0)
1544     {
1545       columns = ovaldef_info_select_columns ();
1546       filter_columns = ovaldef_info_filter_columns ();
1547     }
1548   else if (strcmp (type, "cert_bund_adv") == 0)
1549     {
1550       columns = cert_bund_adv_info_select_columns ();
1551       filter_columns = cert_bund_adv_info_filter_columns ();
1552     }
1553   else if (strcmp (type, "dfn_cert_adv") == 0)
1554     {
1555       columns = dfn_cert_adv_info_select_columns ();
1556       filter_columns = dfn_cert_adv_info_filter_columns ();
1557     }
1558   else
1559     {
1560       g_warning ("%s: Unexpected type %s", __func__, type);
1561       return 0;
1562     }
1563 
1564   if (get_modified)
1565     extra_where = g_strdup_printf (" AND modification_time > %ld"
1566                                    " AND creation_time <= %ld",
1567                                    count_time,
1568                                    count_time);
1569   else
1570     extra_where = g_strdup_printf (" AND creation_time > %ld",
1571                                    count_time);
1572 
1573   ret = count (type, get, (column_t*) columns, NULL, filter_columns,
1574                0, 0, extra_where, FALSE);
1575 
1576   g_free (extra_where);
1577   return ret;
1578 }
1579 
1580 /**
1581  * @brief Initialise an ovaldi file iterator.
1582  *
1583  * @param[in]  iterator        Iterator.
1584  */
1585 void
init_ovaldi_file_iterator(iterator_t * iterator)1586 init_ovaldi_file_iterator (iterator_t* iterator)
1587 {
1588   init_iterator (iterator, "SELECT DISTINCT xml_file FROM ovaldefs;");
1589 }
1590 
1591 /**
1592  * @brief Get the name from an ovaldi file iterator.
1593  *
1594  * @param[in]  iterator  Iterator.
1595  *
1596  * @return The name of the file, or NULL if iteration is complete.  Freed by
1597  *         cleanup_iterator.
1598  */
1599 DEF_ACCESS (ovaldi_file_iterator_name, 0);
1600 
1601 
1602 /* CERT update: DFN-CERT. */
1603 
1604 /**
1605  * @brief Update DFN-CERT info from a single XML feed file.
1606  *
1607  * @param[in]  xml_path          XML path.
1608  * @param[in]  last_cert_update  Time of last CERT update.
1609  * @param[in]  last_dfn_update   Time of last update to a DFN.
1610  *
1611  * @return 0 nothing to do, 1 updated, -1 error.
1612  */
1613 static int
update_dfn_xml(const gchar * xml_path,int last_cert_update,int last_dfn_update)1614 update_dfn_xml (const gchar *xml_path, int last_cert_update,
1615                 int last_dfn_update)
1616 {
1617   GError *error;
1618   element_t element, child;
1619   gchar *xml, *full_path;
1620   gsize xml_len;
1621   GStatBuf state;
1622   int updated_dfn_cert;
1623   int transaction_size = 0;
1624 
1625   updated_dfn_cert = 0;
1626   g_info ("%s: %s", __func__, xml_path);
1627 
1628   full_path = g_build_filename (GVM_CERT_DATA_DIR, xml_path, NULL);
1629 
1630   if (g_stat (full_path, &state))
1631     {
1632       g_warning ("%s: Failed to stat CERT file: %s",
1633                  __func__,
1634                  strerror (errno));
1635       return -1;
1636     }
1637 
1638   if ((state.st_mtime - (state.st_mtime % 60)) <= last_cert_update)
1639     {
1640       g_info ("Skipping %s, file is older than last revision",
1641               full_path);
1642       g_free (full_path);
1643       return 0;
1644     }
1645 
1646   g_info ("Updating %s", full_path);
1647 
1648   error = NULL;
1649   g_file_get_contents (full_path, &xml, &xml_len, &error);
1650   if (error)
1651     {
1652       g_warning ("%s: Failed to get contents: %s",
1653                  __func__,
1654                  error->message);
1655       g_error_free (error);
1656       g_free (full_path);
1657       return -1;
1658     }
1659 
1660   if (parse_element (xml, &element))
1661     {
1662       g_free (xml);
1663       g_warning ("%s: Failed to parse element", __func__);
1664       g_free (full_path);
1665       return -1;
1666     }
1667   g_free (xml);
1668 
1669   sql_begin_immediate ();
1670   child = element_first_child (element);
1671   while (child)
1672     {
1673       if (strcmp (element_name (child), "entry") == 0)
1674         {
1675           element_t updated;
1676           gchar *updated_text;
1677 
1678           updated = element_child (child, "updated");
1679           if (updated == NULL)
1680             {
1681               g_warning ("%s: UPDATED missing", __func__);
1682               element_free (element);
1683               goto fail;
1684             }
1685 
1686           updated_text = element_text (updated);
1687           if (parse_iso_time (updated_text) > last_dfn_update)
1688             {
1689               element_t refnum, published, summary, title, cve;
1690               gchar *quoted_refnum, *quoted_title, *quoted_summary;
1691               int cve_refs;
1692 
1693               refnum = element_child (child, "dfncert:refnum");
1694               if (refnum == NULL)
1695                 {
1696                   g_warning ("%s: REFNUM missing", __func__);
1697                   element_free (element);
1698                   g_free (updated_text);
1699                   goto fail;
1700                 }
1701 
1702               published = element_child (child, "published");
1703               if (published == NULL)
1704                 {
1705                   g_warning ("%s: PUBLISHED missing", __func__);
1706                   element_free (element);
1707                   g_free (updated_text);
1708                   goto fail;
1709                 }
1710 
1711               title = element_child (child, "title");
1712               if (title == NULL)
1713                 {
1714                   g_warning ("%s: TITLE missing", __func__);
1715                   element_free (element);
1716                   g_free (updated_text);
1717                   goto fail;
1718                 }
1719 
1720               summary = element_child (child, "summary");
1721               if (summary == NULL)
1722                 {
1723                   g_warning ("%s: SUMMARY missing", __func__);
1724                   element_free (element);
1725                   g_free (updated_text);
1726                   goto fail;
1727                 }
1728 
1729               cve_refs = 0;
1730               cve = element_first_child (child);
1731               while (cve)
1732                 {
1733                   if (strcmp (element_name (cve), "cve") == 0)
1734                     cve_refs++;
1735                   cve = element_next (cve);
1736                 }
1737 
1738               quoted_refnum = sql_quote_element_text (refnum);
1739               quoted_title = sql_quote_element_text (title);
1740               quoted_summary = sql_quote_element_text (summary);
1741               sql ("INSERT INTO cert.dfn_cert_advs"
1742                    " (uuid, name, comment, creation_time,"
1743                    "  modification_time, title, summary, cve_refs)"
1744                    " VALUES"
1745                    " ('%s', '%s', '', %i, %i, '%s', '%s', %i)"
1746                    " ON CONFLICT (uuid) DO UPDATE"
1747                    " SET name = EXCLUDED.uuid,"
1748                    "     comment = '',"
1749                    "     creation_time = EXCLUDED.creation_time,"
1750                    "     modification_time = EXCLUDED.modification_time,"
1751                    "     title = EXCLUDED.title,"
1752                    "     summary = EXCLUDED.summary,"
1753                    "     cve_refs = EXCLUDED.cve_refs;",
1754                    quoted_refnum,
1755                    quoted_refnum,
1756                    parse_iso_time_element_text (published),
1757                    parse_iso_time (updated_text),
1758                    quoted_title,
1759                    quoted_summary,
1760                    cve_refs);
1761               increment_transaction_size (&transaction_size);
1762               g_free (quoted_title);
1763               g_free (quoted_summary);
1764 
1765               cve = element_first_child (child);
1766               while (cve)
1767                 {
1768                   if (strcmp (element_name (cve), "cve") == 0)
1769                     {
1770                       gchar **split, **point;
1771                       gchar *text, *start;
1772 
1773                       text = element_text (cve);
1774                       start = text;
1775                       while ((start = strstr (start, "CVE ")))
1776                         start[3] = '-';
1777 
1778                       split = g_strsplit (text, " ", 0);
1779                       g_free (text);
1780                       point = split;
1781                       while (*point)
1782                         {
1783                           if (g_str_has_prefix (*point, "CVE-")
1784                               && (strlen (*point) >= 13)
1785                               && atoi (*point + 4) > 0)
1786                             {
1787                               gchar *quoted_point;
1788 
1789                               quoted_point = sql_quote (*point);
1790                               /* There's no primary key, so just INSERT, even
1791                                * for Postgres. */
1792                               sql ("INSERT INTO dfn_cert_cves"
1793                                    " (adv_id, cve_name)"
1794                                    " VALUES"
1795                                    " ((SELECT id FROM dfn_cert_advs"
1796                                    "   WHERE name = '%s'),"
1797                                    "  '%s')",
1798                                    quoted_refnum,
1799                                    quoted_point);
1800                               increment_transaction_size (&transaction_size);
1801                               g_free (quoted_point);
1802                             }
1803                           point++;
1804                         }
1805                       g_strfreev (split);
1806                     }
1807 
1808                   cve = element_next (cve);
1809                 }
1810 
1811               updated_dfn_cert = 1;
1812               g_free (quoted_refnum);
1813             }
1814 
1815           g_free (updated_text);
1816         }
1817       child = element_next (child);
1818     }
1819 
1820   element_free (element);
1821   g_free (full_path);
1822   sql_commit ();
1823   return updated_dfn_cert;
1824 
1825  fail:
1826   g_warning ("Update of DFN-CERT Advisories failed at file '%s'",
1827              full_path);
1828   g_free (full_path);
1829   sql_commit ();
1830   return -1;
1831 }
1832 
1833 /**
1834  * @brief Update DFN-CERTs.
1835  *
1836  * Assume that the databases are attached.
1837  *
1838  * @param[in]  last_cert_update  Time of last CERT update from meta.
1839  *
1840  * @return 0 nothing to do, 1 updated, -1 error.
1841  */
1842 static int
update_dfn_cert_advisories(int last_cert_update)1843 update_dfn_cert_advisories (int last_cert_update)
1844 {
1845   GError *error;
1846   int count, last_dfn_update, updated_dfn_cert;
1847   GDir *dir;
1848   const gchar *xml_path;
1849 
1850   error = NULL;
1851   dir = g_dir_open (GVM_CERT_DATA_DIR, 0, &error);
1852   if (dir == NULL)
1853     {
1854       g_warning ("%s: Failed to open directory '%s': %s",
1855                  __func__, GVM_CERT_DATA_DIR, error->message);
1856       g_error_free (error);
1857       return -1;
1858     }
1859 
1860   last_dfn_update = sql_int ("SELECT max (modification_time)"
1861                              " FROM cert.dfn_cert_advs;");
1862 
1863   g_debug ("%s: VS: " GVM_CERT_DATA_DIR "/dfn-cert-*.xml", __func__);
1864   count = 0;
1865   updated_dfn_cert = 0;
1866   while ((xml_path = g_dir_read_name (dir)))
1867     if (fnmatch ("dfn-cert-*.xml", xml_path, 0) == 0)
1868       {
1869         switch (update_dfn_xml (xml_path, last_cert_update, last_dfn_update))
1870           {
1871             case 0:
1872               break;
1873             case 1:
1874               updated_dfn_cert = 1;
1875               break;
1876             default:
1877               g_dir_close (dir);
1878               return -1;
1879           }
1880         count++;
1881       }
1882 
1883   if (count == 0)
1884     g_warning ("No DFN-CERT advisories found in %s", GVM_CERT_DATA_DIR);
1885 
1886   g_dir_close (dir);
1887   return updated_dfn_cert;
1888 }
1889 
1890 
1891 /* CERT update: CERT-BUND. */
1892 
1893 /**
1894  * @brief Update CERT-Bund info from a single XML feed file.
1895  *
1896  * @param[in]  xml_path          XML path.
1897  * @param[in]  last_cert_update  Time of last CERT update.
1898  * @param[in]  last_bund_update   Time of last update to a DFN.
1899  *
1900  * @return 0 nothing to do, 1 updated, -1 error.
1901  */
1902 static int
update_bund_xml(const gchar * xml_path,int last_cert_update,int last_bund_update)1903 update_bund_xml (const gchar *xml_path, int last_cert_update,
1904                  int last_bund_update)
1905 {
1906   GError *error;
1907   element_t element, child;
1908   gchar *xml, *full_path;
1909   gsize xml_len;
1910   GStatBuf state;
1911   int updated_cert_bund;
1912   int transaction_size = 0;
1913 
1914   updated_cert_bund = 0;
1915   full_path = g_build_filename (GVM_CERT_DATA_DIR, xml_path, NULL);
1916 
1917   if (g_stat (full_path, &state))
1918     {
1919       g_warning ("%s: Failed to stat CERT file: %s",
1920                  __func__,
1921                  strerror (errno));
1922       return -1;
1923     }
1924 
1925   if ((state.st_mtime - (state.st_mtime % 60)) <= last_cert_update)
1926     {
1927       g_info ("Skipping %s, file is older than last revision",
1928               full_path);
1929       g_free (full_path);
1930       return 0;
1931     }
1932 
1933   g_info ("Updating %s", full_path);
1934 
1935   error = NULL;
1936   g_file_get_contents (full_path, &xml, &xml_len, &error);
1937   if (error)
1938     {
1939       g_warning ("%s: Failed to get contents: %s",
1940                  __func__,
1941                  error->message);
1942       g_error_free (error);
1943       g_free (full_path);
1944       return -1;
1945     }
1946 
1947   if (parse_element (xml, &element))
1948     {
1949       g_free (xml);
1950       g_warning ("%s: Failed to parse element", __func__);
1951       g_free (full_path);
1952       return -1;
1953     }
1954   g_free (xml);
1955 
1956   sql_begin_immediate ();
1957   child = element_first_child (element);
1958   while (child)
1959     {
1960       if (strcmp (element_name (child), "Advisory") == 0)
1961         {
1962           element_t date;
1963 
1964           date = element_child (child, "Date");
1965           if (date == NULL)
1966             {
1967               g_warning ("%s: Date missing", __func__);
1968               element_free (element);
1969               goto fail;
1970             }
1971           if (parse_iso_time_element_text (date) > last_bund_update)
1972             {
1973               element_t refnum, description, title, cve, cve_list;
1974               gchar *quoted_refnum, *quoted_title, *quoted_summary;
1975               int cve_refs;
1976               GString *summary;
1977 
1978               refnum = element_child (child, "Ref_Num");
1979               if (refnum == NULL)
1980                 {
1981                   g_warning ("%s: Ref_Num missing", __func__);
1982                   element_free (element);
1983                   goto fail;
1984                 }
1985 
1986               title = element_child (child, "Title");
1987               if (title == NULL)
1988                 {
1989                   g_warning ("%s: Title missing", __func__);
1990                   element_free (element);
1991                   goto fail;
1992                 }
1993 
1994               summary = g_string_new ("");
1995               description = element_child (child, "Description");
1996               if (description)
1997                 {
1998                   element_t delement;
1999 
2000                   delement = element_first_child (description);
2001                   while (delement)
2002                     {
2003                       if (strcmp (element_name (delement), "Element") == 0)
2004                         {
2005                           element_t text_block;
2006                           text_block = element_child (delement, "TextBlock");
2007                           if (text_block)
2008                             {
2009                               gchar *text;
2010 
2011                               text = element_text (text_block);
2012                               g_string_append (summary, text);
2013                               g_free (text);
2014                             }
2015                         }
2016                       delement = element_next (delement);
2017                     }
2018                 }
2019 
2020               cve_refs = 0;
2021               cve_list = element_child (child, "CVEList");
2022               if (cve_list)
2023                 {
2024                   cve = element_first_child (cve_list);
2025                   while (cve)
2026                     {
2027                       if (strcmp (element_name (cve), "CVE") == 0)
2028                         cve_refs++;
2029                       cve = element_next (cve);
2030                     }
2031                 }
2032 
2033               quoted_refnum = sql_quote_element_text (refnum);
2034               quoted_title = sql_quote_element_text (title);
2035               quoted_summary = sql_quote (summary->str);
2036               g_string_free (summary, TRUE);
2037               sql ("INSERT INTO cert.cert_bund_advs"
2038                    " (uuid, name, comment, creation_time,"
2039                    "  modification_time, title, summary, cve_refs)"
2040                    " VALUES"
2041                    " ('%s', '%s', '', %i, %i, '%s', '%s', %i)"
2042                    " ON CONFLICT (uuid) DO UPDATE"
2043                    " SET name = EXCLUDED.uuid,"
2044                    "     comment = '',"
2045                    "     creation_time = EXCLUDED.creation_time,"
2046                    "     modification_time = EXCLUDED.modification_time,"
2047                    "     title = EXCLUDED.title,"
2048                    "     summary = EXCLUDED.summary,"
2049                    "     cve_refs = EXCLUDED.cve_refs;",
2050                    quoted_refnum,
2051                    quoted_refnum,
2052                    parse_iso_time_element_text (date),
2053                    parse_iso_time_element_text (date),
2054                    quoted_title,
2055                    quoted_summary,
2056                    cve_refs);
2057               increment_transaction_size (&transaction_size);
2058               g_free (quoted_title);
2059               g_free (quoted_summary);
2060 
2061               cve_list = element_child (child, "CVEList");
2062               if (cve_list)
2063                 {
2064                   cve = element_first_child (cve_list);
2065                   while (cve)
2066                     {
2067                       if (strcmp (element_name (cve), "CVE") == 0)
2068                         {
2069                           gchar *cve_text;
2070 
2071                           cve_text = element_text (cve);
2072 
2073                           if (strlen (cve_text))
2074                             {
2075                               gchar *quoted_cve;
2076                               quoted_cve = sql_quote (cve_text);
2077                               /* There's no primary key, so just INSERT, even
2078                                * for Postgres. */
2079                               sql ("INSERT INTO cert_bund_cves"
2080                                    " (adv_id, cve_name)"
2081                                    " VALUES"
2082                                    " ((SELECT id FROM cert_bund_advs"
2083                                    "   WHERE name = '%s'),"
2084                                    "  '%s')",
2085                                    quoted_refnum,
2086                                    quoted_cve);
2087                               increment_transaction_size (&transaction_size);
2088                               g_free (quoted_cve);
2089                             }
2090                           g_free (cve_text);
2091                         }
2092 
2093                       cve = element_next (cve);
2094                     }
2095                 }
2096 
2097               updated_cert_bund = 1;
2098               g_free (quoted_refnum);
2099             }
2100         }
2101       child = element_next (child);
2102     }
2103 
2104   element_free (element);
2105   g_free (full_path);
2106   sql_commit ();
2107   return updated_cert_bund;
2108 
2109  fail:
2110   g_warning ("Update of CERT-Bund Advisories failed at file '%s'",
2111              full_path);
2112   g_free (full_path);
2113   sql_commit ();
2114   return -1;
2115 }
2116 
2117 /**
2118  * @brief Update CERT-Bunds.
2119  *
2120  * Assume that the databases are attached.
2121  *
2122  * @param[in]  last_cert_update  Time of last CERT update from meta.
2123  *
2124  * @return 0 nothing to do, 1 updated, -1 error.
2125  */
2126 static int
update_cert_bund_advisories(int last_cert_update)2127 update_cert_bund_advisories (int last_cert_update)
2128 {
2129   GError *error;
2130   int count, last_bund_update, updated_cert_bund;
2131   GDir *dir;
2132   const gchar *xml_path;
2133 
2134   error = NULL;
2135   dir = g_dir_open (GVM_CERT_DATA_DIR, 0, &error);
2136   if (dir == NULL)
2137     {
2138       g_warning ("%s: Failed to open directory '%s': %s",
2139                  __func__, GVM_CERT_DATA_DIR, error->message);
2140       g_error_free (error);
2141       return -1;
2142     }
2143 
2144   last_bund_update = sql_int ("SELECT max (modification_time)"
2145                               " FROM cert.cert_bund_advs;");
2146 
2147   count = 0;
2148   updated_cert_bund = 0;
2149   while ((xml_path = g_dir_read_name (dir)))
2150     if (fnmatch ("CB-K*.xml", xml_path, 0) == 0)
2151       {
2152         switch (update_bund_xml (xml_path, last_cert_update, last_bund_update))
2153           {
2154             case 0:
2155               break;
2156             case 1:
2157               updated_cert_bund = 1;
2158               break;
2159             default:
2160               g_dir_close (dir);
2161               return -1;
2162           }
2163         count++;
2164       }
2165 
2166   if (count == 0)
2167     g_warning ("No CERT-Bund advisories found in %s", GVM_CERT_DATA_DIR);
2168 
2169   g_dir_close (dir);
2170   return updated_cert_bund;
2171 }
2172 
2173 
2174 /* SCAP update: CPEs. */
2175 
2176 /**
2177  * @brief Insert a SCAP CPE.
2178  *
2179  * @param[in]  inserts            Pointer to SQL buffer.
2180  * @param[in]  cpe_item           CPE item XML element.
2181  * @param[in]  item_metadata      Item's metadata element.
2182  * @param[in]  modification_time  Modification time of item.
2183  *
2184  * @return 0 success, -1 error.
2185  */
2186 static int
insert_scap_cpe(inserts_t * inserts,element_t cpe_item,element_t item_metadata,int modification_time)2187 insert_scap_cpe (inserts_t *inserts, element_t cpe_item, element_t item_metadata,
2188                  int modification_time)
2189 {
2190   gchar *name, *status, *deprecated, *nvd_id;
2191   gchar *quoted_name, *quoted_title, *quoted_status, *quoted_nvd_id;
2192   gchar *name_decoded, *name_tilde;
2193   element_t title;
2194   int first;
2195 
2196   assert (inserts);
2197 
2198   name = element_attribute (cpe_item, "name");
2199   if (name == NULL)
2200     {
2201       g_warning ("%s: name missing", __func__);
2202       return -1;
2203     }
2204 
2205   status = element_attribute (item_metadata, "status");
2206   if (status == NULL)
2207     {
2208       g_warning ("%s: status missing", __func__);
2209       g_free (name);
2210       return -1;
2211     }
2212 
2213   deprecated = element_attribute (item_metadata,
2214                                  "deprecated-by-nvd-id");
2215   if (deprecated
2216       && (g_regex_match_simple ("^[0-9]+$", (gchar *) deprecated, 0, 0)
2217           == 0))
2218     {
2219       g_warning ("%s: invalid deprecated-by-nvd-id: %s",
2220                  __func__,
2221                  deprecated);
2222       g_free (name);
2223       g_free (status);
2224       return -1;
2225     }
2226 
2227   nvd_id = element_attribute (item_metadata, "nvd-id");
2228   if (nvd_id == NULL)
2229     {
2230       g_warning ("%s: nvd_id missing", __func__);
2231       g_free (name);
2232       g_free (status);
2233       g_free (deprecated);
2234       return -1;
2235     }
2236 
2237   title = element_first_child (cpe_item);
2238   quoted_title = g_strdup ("");
2239   while (title)
2240     {
2241       if (strcmp (element_name (title), "title") == 0)
2242         {
2243           gchar *lang;
2244 
2245           lang = element_attribute (title, "xml:lang");
2246           if (lang && strcmp (lang, "en-US") == 0)
2247             {
2248               gchar *title_text;
2249 
2250               title_text = element_text (title);
2251               g_free (quoted_title);
2252               quoted_title = sql_quote (title_text);
2253               g_free (title_text);
2254 
2255               g_free (lang);
2256               break;
2257             }
2258           g_free (lang);
2259         }
2260       title = element_next (title);
2261     }
2262 
2263   name_decoded = g_uri_unescape_string (name, NULL);
2264   g_free (name);
2265   name_tilde = string_replace (name_decoded,
2266                                "~", "%7E", "%7e", NULL);
2267   g_free (name_decoded);
2268   quoted_name = sql_quote (name_tilde);
2269   g_free (name_tilde);
2270   quoted_status = sql_quote (status);
2271   g_free (status);
2272   quoted_nvd_id = sql_quote (nvd_id);
2273   g_free (nvd_id);
2274 
2275   first = inserts_check_size (inserts);
2276 
2277   g_string_append_printf (inserts->statement,
2278                           "%s ('%s', '%s', '%s', %i, %i, '%s', %s, '%s')",
2279                           first ? "" : ",",
2280                           quoted_name,
2281                           quoted_name,
2282                           quoted_title,
2283                           modification_time,
2284                           modification_time,
2285                           quoted_status,
2286                           deprecated ? deprecated : "NULL",
2287                           quoted_nvd_id);
2288 
2289   inserts->current_chunk_size++;
2290 
2291   g_free (quoted_title);
2292   g_free (quoted_name);
2293   g_free (quoted_status);
2294   g_free (quoted_nvd_id);
2295   g_free (deprecated);
2296 
2297   return 0;
2298 }
2299 
2300 /**
2301  * @brief Update SCAP CPEs from a file.
2302  *
2303  * @param[in]  path             Path to file.
2304  *
2305  * @return 0 success, -1 error.
2306  */
2307 static int
update_scap_cpes_from_file(const gchar * path)2308 update_scap_cpes_from_file (const gchar *path)
2309 {
2310   GError *error;
2311   element_t element, cpe_list, cpe_item;
2312   gchar *xml;
2313   gsize xml_len;
2314   inserts_t inserts;
2315 
2316   g_debug ("%s: parsing %s", __func__, path);
2317 
2318   error = NULL;
2319   g_file_get_contents (path, &xml, &xml_len, &error);
2320   if (error)
2321     {
2322       g_warning ("%s: Failed to get contents: %s",
2323                  __func__,
2324                  error->message);
2325       g_error_free (error);
2326       return -1;
2327     }
2328 
2329   if (parse_element (xml, &element))
2330     {
2331       g_free (xml);
2332       g_warning ("%s: Failed to parse element", __func__);
2333       return -1;
2334     }
2335   g_free (xml);
2336 
2337   cpe_list = element;
2338   if (strcmp (element_name (cpe_list), "cpe-list"))
2339     {
2340       element_free (element);
2341       g_warning ("%s: CPE dictionary missing CPE-LIST", __func__);
2342       return -1;
2343     }
2344 
2345   sql_begin_immediate ();
2346 
2347   inserts_init (&inserts,
2348                 CPE_MAX_CHUNK_SIZE,
2349                 "INSERT INTO scap2.cpes"
2350                 " (uuid, name, title, creation_time,"
2351                 "  modification_time, status, deprecated_by_id,"
2352                 "  nvd_id)"
2353                 " VALUES",
2354                 " ON CONFLICT (uuid) DO UPDATE"
2355                 " SET name = EXCLUDED.name,"
2356                 "     title = EXCLUDED.title,"
2357                 "     creation_time = EXCLUDED.creation_time,"
2358                 "     modification_time = EXCLUDED.modification_time,"
2359                 "     status = EXCLUDED.status,"
2360                 "     deprecated_by_id = EXCLUDED.deprecated_by_id,"
2361                 "     nvd_id = EXCLUDED.nvd_id");
2362   cpe_item = element_first_child (cpe_list);
2363   while (cpe_item)
2364     {
2365       gchar *modification_date;
2366       int modification_time;
2367       element_t item_metadata;
2368 
2369       if (strcmp (element_name (cpe_item), "cpe-item"))
2370         {
2371           cpe_item = element_next (cpe_item);
2372           continue;
2373         }
2374 
2375       item_metadata = element_child (cpe_item, "meta:item-metadata");
2376       if (item_metadata == NULL)
2377         {
2378           g_warning ("%s: item-metadata missing", __func__);
2379           goto fail;
2380         }
2381 
2382       modification_date = element_attribute (item_metadata,
2383                                             "modification-date");
2384       if (modification_date == NULL)
2385         {
2386           g_warning ("%s: modification-date missing", __func__);
2387           goto fail;
2388         }
2389 
2390       modification_time = parse_iso_time (modification_date);
2391       g_free (modification_date);
2392 
2393       if (insert_scap_cpe (&inserts, cpe_item, item_metadata,
2394                            modification_time))
2395         goto fail;
2396       cpe_item = element_next (cpe_item);
2397     }
2398 
2399   element_free (element);
2400 
2401   inserts_run (&inserts);
2402 
2403   sql_commit ();
2404   return 0;
2405 
2406  fail:
2407   inserts_free (&inserts);
2408   element_free (element);
2409   g_warning ("Update of CPEs failed");
2410   sql_commit ();
2411   return -1;
2412 }
2413 
2414 /**
2415  * @brief Update SCAP CPEs.
2416  *
2417  * @return 0 success, -1 error.
2418  */
2419 static int
update_scap_cpes()2420 update_scap_cpes ()
2421 {
2422   gchar *full_path;
2423   const gchar *split_dir;
2424   GStatBuf state;
2425   int index;
2426 
2427   full_path = g_build_filename (GVM_SCAP_DATA_DIR,
2428                                 "official-cpe-dictionary_v2.2.xml",
2429                                 NULL);
2430 
2431   if (g_stat (full_path, &state))
2432     {
2433       g_warning ("%s: No CPE dictionary found at %s",
2434                  __func__,
2435                  full_path);
2436       g_free (full_path);
2437       return -1;
2438     }
2439 
2440   g_info ("Updating CPEs");
2441 
2442   split_dir = split_xml_file (full_path, "40Mb", "</cpe-list>");
2443   if (split_dir == NULL)
2444     {
2445       int ret;
2446 
2447       g_warning ("%s: Failed to split CPEs, attempting with full file",
2448                  __func__);
2449       ret = update_scap_cpes_from_file (full_path);
2450       g_free (full_path);
2451       return ret;
2452     }
2453   g_free (full_path);
2454 
2455   for (index = 1; 1; index++)
2456     {
2457       int ret;
2458       gchar *path, *name;
2459 
2460       name = g_strdup_printf ("split-%02i.xml", index);
2461       path = g_build_filename (split_dir, name, NULL);
2462       g_free (name);
2463 
2464       if (g_stat (path, &state))
2465         {
2466           g_free (path);
2467           break;
2468         }
2469 
2470       ret = update_scap_cpes_from_file (path);
2471       g_free (path);
2472       if (ret < 0)
2473         {
2474           gvm_file_remove_recurse (split_dir);
2475           return -1;
2476         }
2477     }
2478 
2479   gvm_file_remove_recurse (split_dir);
2480 
2481   return 0;
2482 }
2483 
2484 
2485 /* SCAP update: CVEs. */
2486 
2487 /**
2488  * @brief Check if this is the last appearance of a product in its siblings.
2489  *
2490  * @param[in]  product  Product.
2491  *
2492  * @return 1 if last appearance of product, else 0.
2493  */
2494 static int
last_appearance(element_t product)2495 last_appearance (element_t product)
2496 {
2497   element_t product2;
2498 
2499   product2 = element_next (product);
2500   while (product2)
2501     {
2502       gchar *product_text, *product2_text;
2503       int cmp;
2504 
2505       product_text = element_text (product);
2506       product2_text = element_text (product2);
2507 
2508       cmp = strcmp (product_text, product2_text);
2509       g_free (product_text);
2510       g_free (product2_text);
2511       if (cmp == 0)
2512         break;
2513       product2 = element_next (product2);
2514     }
2515   return product2 == NULL;
2516 }
2517 
2518 /**
2519  * @brief Get the ID of a CPE from a hashtable.
2520  *
2521  * @param[in]  hashed_cpes    CPEs.
2522  * @param[in]  product_tilde  UUID/Name.
2523  *
2524  * @return ID of CPE from hashtable.
2525  */
2526 static int
hashed_cpes_cpe_id(GHashTable * hashed_cpes,const gchar * product_tilde)2527 hashed_cpes_cpe_id (GHashTable *hashed_cpes, const gchar *product_tilde)
2528 {
2529   return GPOINTER_TO_INT (g_hash_table_lookup (hashed_cpes, product_tilde));
2530 }
2531 
2532 /**
2533  * @brief Insert products for a CVE.
2534  *
2535  * @param[in]  list              XML product list.
2536  * @param[in]  cve               CVE.
2537  * @param[in]  time_published    Time published.
2538  * @param[in]  time_modified     Time modified.
2539  * @param[in]  hashed_cpes       Hashed CPEs.
2540  * @param[in]  transaction_size  Statement counter for batching.
2541  */
2542 static void
insert_cve_products(element_t list,resource_t cve,int time_modified,int time_published,GHashTable * hashed_cpes,int * transaction_size)2543 insert_cve_products (element_t list, resource_t cve,
2544                      int time_modified, int time_published,
2545                      GHashTable *hashed_cpes, int *transaction_size)
2546 {
2547   element_t product;
2548   int first_product, first_affected;
2549   GString *sql_cpes, *sql_affected;
2550 
2551   if (list == NULL)
2552     return;
2553 
2554   product = element_first_child (list);
2555 
2556   if (product == NULL)
2557     return;
2558 
2559   sql_cpes = g_string_new ("INSERT INTO scap2.cpes"
2560                            " (uuid, name, creation_time,"
2561                            "  modification_time)"
2562                            " VALUES");
2563   sql_affected = g_string_new ("INSERT INTO scap2.affected_products"
2564                                " (cve, cpe)"
2565                                " VALUES");
2566 
2567   /* Buffer the SQL. */
2568 
2569   first_product = first_affected = 1;
2570 
2571   while (product)
2572     {
2573       gchar *product_text;
2574 
2575       if (strcmp (element_name (product), "product"))
2576         {
2577           product = element_next (product);
2578           continue;
2579         }
2580 
2581       product_text = element_text (product);
2582       if (strlen (product_text))
2583         {
2584           gchar *quoted_product, *product_decoded;
2585           gchar *product_tilde;
2586 
2587           product_decoded = g_uri_unescape_string
2588                              (element_text (product), NULL);
2589           product_tilde = string_replace (product_decoded,
2590                                           "~", "%7E", "%7e",
2591                                           NULL);
2592           g_free (product_decoded);
2593           quoted_product = sql_quote (product_tilde);
2594 
2595           if (g_hash_table_contains (hashed_cpes, product_tilde) == 0)
2596             {
2597               /* The product was not in the db.
2598                *
2599                * Only insert the product if this is its last appearance
2600                * in the current CVE's XML, to avoid errors from Postgres
2601                * ON CONFLICT DO UPDATE. */
2602 
2603               if (last_appearance (product))
2604                 {
2605                   /* The CPE does not appear later in this CVE's XML. */
2606 
2607                   g_string_append_printf
2608                    (sql_cpes,
2609                     "%s ('%s', '%s', %i, %i)",
2610                     first_product ? "" : ",", quoted_product, quoted_product,
2611                     time_published, time_modified);
2612 
2613                   first_product = 0;
2614 
2615                   /* We could add product_tilde to the hashtable but then we
2616                    * would have to worry about memory management in the
2617                    * hashtable. */
2618                 }
2619 
2620               /* We don't know the db id of the CPE right now. */
2621 
2622               g_string_append_printf
2623                (sql_affected,
2624                 "%s (%llu,"
2625                 "    (SELECT id FROM scap2.cpes"
2626                 "     WHERE name='%s'))",
2627                 first_affected ? "" : ",", cve, quoted_product);
2628             }
2629           else
2630             {
2631               int cpe;
2632 
2633               /* The product is in the db.
2634                *
2635                * So we don't need to insert it. */
2636 
2637               cpe = hashed_cpes_cpe_id (hashed_cpes, product_tilde);
2638 
2639               g_string_append_printf
2640                (sql_affected,
2641                 "%s (%llu, %i)",
2642                 first_affected ? "" : ",", cve,
2643                 cpe);
2644             }
2645 
2646           first_affected = 0;
2647           g_free (product_tilde);
2648           g_free (quoted_product);
2649         }
2650 
2651       g_free (product_text);
2652 
2653       product = element_next (product);
2654     }
2655 
2656   /* Run the SQL. */
2657 
2658   if (first_product == 0)
2659      {
2660        sql ("%s"
2661             " ON CONFLICT (uuid)"
2662             " DO UPDATE SET name = EXCLUDED.name;",
2663             sql_cpes->str);
2664 
2665        increment_transaction_size (transaction_size);
2666      }
2667 
2668    if (first_affected == 0)
2669      {
2670        sql ("%s"
2671             " ON CONFLICT DO NOTHING;",
2672             sql_affected->str);
2673 
2674        increment_transaction_size (transaction_size);
2675      }
2676 
2677    g_string_free (sql_cpes, TRUE);
2678    g_string_free (sql_affected, TRUE);
2679 }
2680 
2681 /**
2682  * @brief Insert a CVE.
2683  *
2684  * @param[in]  entry             XML entry.
2685  * @param[in]  last_modified     XML last_modified element.
2686  * @param[in]  transaction_size  Statement counter for batching.
2687  * @param[in]  hashed_cpes       Hashed CPEs.
2688  *
2689  * @return 0 success, -1 error.
2690  */
2691 static int
insert_cve_from_entry(element_t entry,element_t last_modified,GHashTable * hashed_cpes,int * transaction_size)2692 insert_cve_from_entry (element_t entry, element_t last_modified,
2693                        GHashTable *hashed_cpes, int *transaction_size)
2694 {
2695   gboolean cvss_is_v3;
2696   element_t published, summary, cvss, score, base_metrics, cvss_vector, list;
2697   double severity_dbl;
2698   gchar *quoted_id, *quoted_summary, *quoted_cvss_vector;
2699   gchar *quoted_software, *id;
2700   GString *software;
2701   gchar *software_unescaped, *software_tilde;
2702   int time_modified, time_published;
2703   resource_t cve;
2704 
2705   id = element_attribute (entry, "id");
2706   if (id == NULL)
2707     {
2708       g_warning ("%s: id missing",
2709                  __func__);
2710       return -1;
2711     }
2712 
2713   published = element_child (entry, "vuln:published-datetime");
2714   if (published == NULL)
2715     {
2716       g_warning ("%s: vuln:published-datetime missing",
2717                  __func__);
2718       g_free (id);
2719       return -1;
2720     }
2721 
2722   cvss = element_child (entry, "vuln:cvss3");
2723   if (cvss == NULL)
2724     {
2725       cvss = element_child (entry, "vuln:cvss");
2726       cvss_is_v3 = FALSE;
2727     }
2728   else
2729      cvss_is_v3 = TRUE;
2730 
2731   if (cvss == NULL)
2732     base_metrics = NULL;
2733   else
2734     base_metrics = element_child (cvss,
2735                                   cvss_is_v3 ? "cvss3:base_metrics"
2736                                              : "cvss:base_metrics");
2737 
2738   if (base_metrics == NULL)
2739     {
2740       score = NULL;
2741       cvss_vector = NULL;
2742     }
2743   else
2744     {
2745       score = element_child (base_metrics,
2746                              cvss_is_v3 ? "cvss3:base-score" : "cvss:score");
2747       if (score == NULL)
2748         {
2749           g_warning ("%s: cvss:score missing", __func__);
2750           g_free (id);
2751           return -1;
2752         }
2753 
2754       cvss_vector = element_child (base_metrics,
2755                                    cvss_is_v3 ? "cvss3:vector-string"
2756                                               : "cvss:vector-string");
2757       if (cvss_vector == NULL)
2758         {
2759           g_warning ("%s: cvss:access-vector missing", __func__);
2760           g_free (id);
2761           return -1;
2762         }
2763     }
2764 
2765   if (score == NULL)
2766     severity_dbl = 0;
2767   else
2768     severity_dbl = atof (element_text (score));
2769 
2770   summary = element_child (entry, "vuln:summary");
2771   if (summary == NULL)
2772     {
2773       g_warning ("%s: vuln:summary missing", __func__);
2774       g_free (id);
2775       return -1;
2776     }
2777 
2778   software = g_string_new ("");
2779   list = element_child (entry, "vuln:vulnerable-software-list");
2780   if (list)
2781     {
2782       element_t product;
2783       product = element_first_child (list);
2784       while (product)
2785         {
2786           if (strcmp (element_name (product), "product") == 0)
2787             {
2788               gchar *product_text;
2789 
2790               product_text = element_text (product);
2791               g_string_append_printf (software, "%s ", product_text);
2792               g_free (product_text);
2793             }
2794           product = element_next (product);
2795         }
2796     }
2797 
2798   quoted_id = sql_quote (id);
2799   g_free (id);
2800   quoted_summary = sql_quote_element_text (summary);
2801   quoted_cvss_vector = sql_quote_element_text (cvss_vector);
2802   software_unescaped = g_uri_unescape_string (software->str, NULL);
2803   g_string_free (software, TRUE);
2804   software_tilde = string_replace (software_unescaped,
2805                                    "~", "%7E", "%7e", NULL);
2806   g_free (software_unescaped);
2807   quoted_software = sql_quote (software_tilde);
2808   g_free (software_tilde);
2809   time_modified = parse_iso_time_element_text (last_modified);
2810   time_published = parse_iso_time_element_text (published);
2811   cve = sql_int64_0
2812          ("INSERT INTO scap2.cves"
2813           " (uuid, name, creation_time, modification_time,"
2814           "  severity, description, cvss_vector, products)"
2815           " VALUES"
2816           " ('%s', '%s', %i, %i,"
2817           "  %0.1f, '%s', '%s', '%s')"
2818           " ON CONFLICT (uuid) DO UPDATE"
2819           " SET name = EXCLUDED.name,"
2820           "     creation_time = EXCLUDED.creation_time,"
2821           "     modification_time = EXCLUDED.modification_time,"
2822           "     severity = EXCLUDED.severity,"
2823           "     description = EXCLUDED.description,"
2824           "     cvss_vector = EXCLUDED.cvss_vector,"
2825           "     products = EXCLUDED.products"
2826           " RETURNING scap2.cves.id;",
2827           quoted_id,
2828           quoted_id,
2829           time_published,
2830           time_modified,
2831           severity_dbl,
2832           quoted_summary,
2833           quoted_cvss_vector,
2834           quoted_software);
2835   increment_transaction_size (transaction_size);
2836   g_free (quoted_summary);
2837   g_free (quoted_cvss_vector);
2838 
2839   insert_cve_products (list, cve, time_published, time_modified,
2840                        hashed_cpes, transaction_size);
2841 
2842   g_free (quoted_id);
2843   return 0;
2844 }
2845 
2846 /**
2847  * @brief Update CVE info from a single XML feed file.
2848  *
2849  * @param[in]  xml_path          XML path.
2850  * @param[in]  hashed_cpes       Hashed CPEs.
2851  *
2852  * @return 0 success, -1 error.
2853  */
2854 static int
update_cve_xml(const gchar * xml_path,GHashTable * hashed_cpes)2855 update_cve_xml (const gchar *xml_path, GHashTable *hashed_cpes)
2856 {
2857   GError *error;
2858   element_t element, entry;
2859   gchar *xml, *full_path;
2860   gsize xml_len;
2861   GStatBuf state;
2862   int transaction_size = 0;
2863 
2864   full_path = g_build_filename (GVM_SCAP_DATA_DIR, xml_path, NULL);
2865 
2866   if (g_stat (full_path, &state))
2867     {
2868       g_warning ("%s: Failed to stat SCAP file: %s",
2869                  __func__,
2870                  strerror (errno));
2871       return -1;
2872     }
2873 
2874   g_info ("Updating %s", full_path);
2875 
2876   error = NULL;
2877   g_file_get_contents (full_path, &xml, &xml_len, &error);
2878   if (error)
2879     {
2880       g_warning ("%s: Failed to get contents: %s",
2881                  __func__,
2882                  error->message);
2883       g_error_free (error);
2884       g_free (full_path);
2885       return -1;
2886     }
2887 
2888   if (parse_element (xml, &element))
2889     {
2890       g_free (xml);
2891       g_warning ("%s: Failed to parse element", __func__);
2892       g_free (full_path);
2893       return -1;
2894     }
2895   g_free (xml);
2896 
2897   sql_begin_immediate ();
2898   entry = element_first_child (element);
2899   while (entry)
2900     {
2901       if (strcmp (element_name (entry), "entry") == 0)
2902         {
2903           element_t last_modified;
2904 
2905           last_modified = element_child (entry, "vuln:last-modified-datetime");
2906           if (last_modified == NULL)
2907             {
2908               g_warning ("%s: vuln:last-modified-datetime missing",
2909                          __func__);
2910               goto fail;
2911             }
2912 
2913           if (insert_cve_from_entry (entry, last_modified, hashed_cpes,
2914                                      &transaction_size))
2915             goto fail;
2916         }
2917       entry = element_next (entry);
2918     }
2919 
2920   element_free (element);
2921   g_free (full_path);
2922   sql_commit ();
2923   return 0;
2924 
2925  fail:
2926   element_free (element);
2927   g_warning ("Update of CVEs failed at file '%s'",
2928              full_path);
2929   g_free (full_path);
2930   sql_commit ();
2931   return -1;
2932 }
2933 
2934 /**
2935  * @brief Update SCAP CVEs.
2936  *
2937  * Assume that the databases are attached.
2938  *
2939  * @return 0 success, -1 error.
2940  */
2941 static int
update_scap_cves()2942 update_scap_cves ()
2943 {
2944   GError *error;
2945   int count;
2946   GDir *dir;
2947   const gchar *xml_path;
2948   GHashTable *hashed_cpes;
2949   iterator_t cpes;
2950 
2951   error = NULL;
2952   dir = g_dir_open (GVM_SCAP_DATA_DIR, 0, &error);
2953   if (dir == NULL)
2954     {
2955       g_warning ("%s: Failed to open directory '%s': %s",
2956                  __func__, GVM_SCAP_DATA_DIR, error->message);
2957       g_error_free (error);
2958       return -1;
2959     }
2960 
2961   hashed_cpes = g_hash_table_new (g_str_hash, g_str_equal);
2962   init_iterator (&cpes, "SELECT uuid, id FROM scap2.cpes;");
2963   while (next (&cpes))
2964     g_hash_table_insert (hashed_cpes,
2965                          (gpointer*) iterator_string (&cpes, 0),
2966                          GINT_TO_POINTER (iterator_int (&cpes, 1)));
2967 
2968   count = 0;
2969   while ((xml_path = g_dir_read_name (dir)))
2970     if (fnmatch ("nvdcve-2.0-*.xml", xml_path, 0) == 0)
2971       {
2972         if (update_cve_xml (xml_path, hashed_cpes))
2973           {
2974             g_dir_close (dir);
2975             g_hash_table_destroy (hashed_cpes);
2976             cleanup_iterator (&cpes);
2977             return -1;
2978           }
2979         count++;
2980       }
2981 
2982   if (count == 0)
2983     g_warning ("No CVEs found in %s", GVM_SCAP_DATA_DIR);
2984 
2985   g_dir_close (dir);
2986   g_hash_table_destroy (hashed_cpes);
2987   cleanup_iterator (&cpes);
2988   return 0;
2989 }
2990 
2991 
2992 /* SCAP update: OVAL. */
2993 
2994 /**
2995  * @brief Get last date from definition element.
2996  *
2997  * @param[in]  definition              Definition.
2998  * @param[out] definition_date_newest  Newest date.
2999  * @param[out] definition_date_oldest  Oldest date.
3000  */
3001 static void
oval_definition_dates(element_t definition,int * definition_date_newest,int * definition_date_oldest)3002 oval_definition_dates (element_t definition, int *definition_date_newest,
3003                        int *definition_date_oldest)
3004 {
3005   element_t metadata, oval_repository, date, dates;
3006   int first;
3007   gchar *oldest, *newest;
3008 
3009   assert (definition_date_newest);
3010   assert (definition_date_oldest);
3011 
3012   *definition_date_newest = 0;
3013   *definition_date_oldest = 0;
3014 
3015   metadata = element_child (definition, "metadata");
3016   if (metadata == NULL)
3017     {
3018       g_warning ("%s: metadata missing",
3019                  __func__);
3020       return;
3021     }
3022 
3023   oval_repository = element_child (metadata, "oval_repository");
3024   if (oval_repository == NULL)
3025     {
3026       g_warning ("%s: oval_repository missing",
3027                  __func__);
3028       return;
3029     }
3030 
3031   dates = element_child (oval_repository, "dates");
3032   if (dates == NULL)
3033     {
3034       g_warning ("%s: dates missing",
3035                  __func__);
3036       return;
3037     }
3038 
3039   newest = NULL;
3040   oldest = NULL;
3041   first = 1;
3042   date = element_first_child (dates);
3043   while (date)
3044     {
3045       if ((strcmp (element_name (date), "submitted") == 0)
3046           || (strcmp (element_name (date), "status_change") == 0)
3047           || (strcmp (element_name (date), "modified") == 0))
3048         {
3049           if (first)
3050             {
3051               g_free (newest);
3052               newest = element_attribute (date, "date");
3053               first = 0;
3054             }
3055           g_free (oldest);
3056           oldest = element_attribute (date, "date");
3057         }
3058       date = element_next (date);
3059     }
3060 
3061   if (newest)
3062     {
3063       *definition_date_newest = parse_iso_time (newest);
3064       g_free (newest);
3065     }
3066   if (oldest)
3067     {
3068       *definition_date_oldest = parse_iso_time (oldest);
3069       g_free (oldest);
3070     }
3071 }
3072 
3073 /**
3074  * @brief Get generator/timestamp from main oval_definitions element.
3075  *
3076  * @param[in]  element         Element.
3077  * @param[out] file_timestamp  Timestamp.
3078  */
3079 static void
oval_oval_definitions_date(element_t element,int * file_timestamp)3080 oval_oval_definitions_date (element_t element, int *file_timestamp)
3081 {
3082   element_t generator, timestamp;
3083 
3084   assert (file_timestamp);
3085 
3086   *file_timestamp = 0;
3087 
3088   generator = element_child (element, "generator");
3089   if (generator == NULL)
3090     {
3091       g_warning ("%s: generator missing",
3092                  __func__);
3093       return;
3094     }
3095 
3096   timestamp = element_child (generator, "oval:timestamp");
3097   if (timestamp == NULL)
3098     {
3099       g_warning ("%s: oval:timestamp missing",
3100                  __func__);
3101       return;
3102     }
3103 
3104   *file_timestamp = parse_iso_time_element_text (timestamp);
3105 }
3106 
3107 /**
3108  * @brief Verify a OVAL definitions file.
3109  *
3110  * @param[in]  full_path  Full path to the OVAL definitions file to verify.
3111  *
3112  * @return 0 if valid, else -1.
3113  */
3114 static int
verify_oval_file(const gchar * full_path)3115 verify_oval_file (const gchar *full_path)
3116 {
3117   GError *error;
3118   gchar *xml;
3119   gsize xml_len;
3120   element_t element;
3121 
3122   error = NULL;
3123   g_file_get_contents (full_path, &xml, &xml_len, &error);
3124   if (error)
3125     {
3126       g_warning ("%s: Failed to get contents: %s",
3127                  __func__,
3128                  error->message);
3129       g_error_free (error);
3130       return -1;
3131     }
3132 
3133   if (parse_element (xml, &element))
3134     {
3135       g_free (xml);
3136       g_warning ("%s: Failed to parse element", __func__);
3137       return -1;
3138     }
3139   g_free (xml);
3140 
3141   if (strcmp (element_name (element), "oval_definitions") == 0)
3142     {
3143       int definition_count;
3144       element_t definitions;
3145 
3146       definition_count = 0;
3147       definitions = element_first_child (element);
3148       while (definitions)
3149         {
3150           if (strcmp (element_name (definitions), "definitions")
3151               == 0)
3152             {
3153               element_t definition;
3154 
3155               definition = element_first_child (definitions);
3156               while (definition)
3157                 {
3158                   if (strcmp (element_name (definition), "definition")
3159                       == 0)
3160                     definition_count++;
3161                   definition = element_next (definition);
3162                 }
3163             }
3164           definitions = element_next (definitions);
3165         }
3166 
3167       element_free (element);
3168       if (definition_count == 0)
3169         {
3170           g_warning ("%s: No OVAL definitions found", __func__);
3171           return -1;
3172         }
3173       else
3174         return 0;
3175     }
3176 
3177   if (strcmp (element_name (element), "oval_variables") == 0)
3178     {
3179       int variable_count;
3180       element_t variables;
3181 
3182       variable_count = 0;
3183       variables = element_first_child (element);
3184       while (variables)
3185         {
3186           if (strcmp (element_name (variables), "variables")
3187               == 0)
3188             {
3189               element_t variable;
3190 
3191               variable = element_first_child (variables);
3192               while (variable)
3193                 {
3194                   if (strcmp (element_name (variable), "variable")
3195                       == 0)
3196                     variable_count++;
3197                   variable = element_next (variable);
3198                 }
3199             }
3200           variables = element_next (variables);
3201         }
3202 
3203       element_free (element);
3204       if (variable_count == 0)
3205         {
3206           g_warning ("%s: No OVAL variables found", __func__);
3207           return -1;
3208         }
3209       else
3210         return 0;
3211     }
3212 
3213   if (strcmp (element_name (element), "oval_system_characteristics") == 0)
3214     {
3215       g_warning ("%s: File is an OVAL System Characteristics file",
3216                  __func__);
3217       return -1;
3218     }
3219 
3220   if (strcmp (element_name (element), "oval_results") == 0)
3221     {
3222       g_warning ("%s: File is an OVAL Results one",
3223                  __func__);
3224       return -1;
3225     }
3226 
3227   g_warning ("%s: Root tag neither oval_definitions nor oval_variables",
3228              __func__);
3229   element_free (element);
3230   return -1;
3231 }
3232 
3233 /**
3234  * @brief Update OVALDEF info from a single XML feed file.
3235  *
3236  * @param[in]  file_and_date     Array containing XML path and timestamp.
3237  * @param[in]  private           Whether this is from the user's private dir.
3238  *
3239  * @return 0 success, -1 error.
3240  */
3241 static int
update_ovaldef_xml(gchar ** file_and_date,int private)3242 update_ovaldef_xml (gchar **file_and_date, int private)
3243 {
3244   GError *error;
3245   element_t element, child;
3246   const gchar *xml_path;
3247   gchar *xml_basename, *xml, *quoted_xml_basename;
3248   gsize xml_len;
3249   int file_timestamp;
3250   int transaction_size = 0;
3251 
3252   /* Setup variables. */
3253 
3254   xml_path = file_and_date[0];
3255   assert (xml_path);
3256 
3257   g_debug ("%s: xml_path: %s", __func__, xml_path);
3258 
3259   xml_basename = strstr (xml_path, GVM_SCAP_DATA_DIR);
3260   if (xml_basename == NULL)
3261     {
3262       g_warning ("%s: xml_path missing GVM_SCAP_DATA_DIR: %s",
3263                  __func__,
3264                  xml_path);
3265       return -1;
3266     }
3267   xml_basename += strlen (GVM_SCAP_DATA_DIR);
3268 
3269   quoted_xml_basename = sql_quote (xml_basename);
3270 
3271   if (private)
3272     {
3273       /* Validate OVAL file. */
3274 
3275       if (verify_oval_file (xml_path))
3276         {
3277           g_info ("Validation failed for file '%s'",
3278                   xml_path);
3279           g_free (quoted_xml_basename);
3280           return 0;
3281         }
3282     }
3283 
3284   /* Parse XML from the file. */
3285 
3286   g_info ("Updating %s", xml_path);
3287 
3288   error = NULL;
3289   g_file_get_contents (xml_path, &xml, &xml_len, &error);
3290   if (error)
3291     {
3292       g_warning ("%s: Failed to get contents: %s",
3293                  __func__,
3294                  error->message);
3295       g_error_free (error);
3296       g_free (quoted_xml_basename);
3297       return -1;
3298     }
3299 
3300   if (parse_element (xml, &element))
3301     {
3302       g_free (xml);
3303       g_warning ("%s: Failed to parse element", __func__);
3304       g_free (quoted_xml_basename);
3305       return -1;
3306     }
3307   g_free (xml);
3308 
3309   /* Fill the db according to the XML. */
3310 
3311   sql_begin_immediate ();
3312 
3313   sql ("INSERT INTO ovalfiles (xml_file)"
3314        " SELECT '%s' WHERE NOT EXISTS (SELECT * FROM ovalfiles"
3315        "                               WHERE xml_file = '%s');",
3316        quoted_xml_basename,
3317        quoted_xml_basename);
3318 
3319   sql_commit();
3320   sql_begin_immediate();
3321 
3322   oval_oval_definitions_date (element, &file_timestamp);
3323 
3324   child = element_first_child (element);
3325   while (child)
3326     {
3327       element_t definition;
3328 
3329       if (strcmp (element_name (child), "definitions"))
3330         {
3331           child = element_next (child);
3332           continue;
3333         }
3334 
3335       definition = element_first_child (child);
3336       while (definition)
3337         {
3338           if (strcmp (element_name (definition), "definition") == 0)
3339             {
3340               int definition_date_newest, definition_date_oldest;
3341               gchar *quoted_id, *quoted_oval_id;
3342               element_t metadata, title, description, repository, reference;
3343               element_t status;
3344               gchar *deprecated, *version, *id, *id_value, *class;
3345               gchar *quoted_title, *quoted_class, *quoted_description;
3346               gchar *quoted_status, *status_text;
3347               int cve_count;
3348 
3349               /* The newest and oldest of this definition's dates (created,
3350                * modified, etc), from the OVAL XML. */
3351               oval_definition_dates (definition,
3352                                      &definition_date_newest,
3353                                      &definition_date_oldest);
3354 
3355               id_value = element_attribute (definition, "id");
3356               if (id_value == NULL)
3357                 {
3358                   g_warning ("%s: oval_definition missing id",
3359                              __func__);
3360                   element_free (element);
3361                   goto fail;
3362                 }
3363 
3364               metadata = element_child (definition, "metadata");
3365               if (metadata == NULL)
3366                 {
3367                   g_warning ("%s: metadata missing",
3368                              __func__);
3369                   element_free (element);
3370                   g_free (id_value);
3371                   goto fail;
3372                 }
3373 
3374               title = element_child (metadata, "title");
3375               if (title == NULL)
3376                 {
3377                   g_warning ("%s: title missing",
3378                              __func__);
3379                   element_free (element);
3380                   g_free (id_value);
3381                   goto fail;
3382                 }
3383 
3384               description = element_child (metadata, "description");
3385               if (description == NULL)
3386                 {
3387                   g_warning ("%s: description missing",
3388                              __func__);
3389                   element_free (element);
3390                   g_free (id_value);
3391                   goto fail;
3392                 }
3393 
3394               repository = element_child (metadata, "oval_repository");
3395               if (repository == NULL)
3396                 {
3397                   g_warning ("%s: oval_repository missing",
3398                              __func__);
3399                   element_free (element);
3400                   g_free (id_value);
3401                   goto fail;
3402                 }
3403 
3404               cve_count = 0;
3405               reference = element_first_child (metadata);
3406               while (reference)
3407                 {
3408                   if (strcmp (element_name (reference), "reference") == 0)
3409                     {
3410                       gchar *source;
3411 
3412                       source = element_attribute (reference, "source");
3413                       if (source && strcasecmp (source, "cve") == 0)
3414                         cve_count++;
3415                       g_free (source);
3416                     }
3417                   reference = element_next (reference);
3418                 }
3419 
3420               id = g_strdup_printf ("%s_%s", id_value, xml_basename);
3421               quoted_id = sql_quote (id);
3422               g_free (id);
3423               quoted_oval_id = sql_quote (id_value);
3424               g_free (id_value);
3425 
3426               version = element_attribute (definition, "version");
3427               if (g_regex_match_simple ("^[0-9]+$", (gchar *) version, 0, 0) == 0)
3428                 {
3429                   g_warning ("%s: invalid version: %s",
3430                              __func__,
3431                              version);
3432                   element_free (element);
3433                   g_free (version);
3434                   goto fail;
3435                 }
3436 
3437               class = element_attribute (definition, "class");
3438               quoted_class = sql_quote (class);
3439               g_free (class);
3440               quoted_title = sql_quote_element_text (title);
3441               quoted_description = sql_quote_element_text (description);
3442               status = element_child (repository, "status");
3443               deprecated = element_attribute (definition, "deprecated");
3444               status_text = NULL;
3445               if (status)
3446                 status_text = element_text (status);
3447               if (status_text && strlen (status_text))
3448                 quoted_status = sql_quote (status_text);
3449               else if (deprecated && strcasecmp (deprecated, "TRUE"))
3450                 quoted_status = sql_quote ("DEPRECATED");
3451               else
3452                 quoted_status = sql_quote ("");
3453               g_free (status_text);
3454 
3455               sql ("INSERT INTO scap2.ovaldefs"
3456                    " (uuid, name, comment, creation_time,"
3457                    "  modification_time, version, deprecated, def_class,"
3458                    "  title, description, xml_file, status,"
3459                    "  severity, cve_refs)"
3460                    " VALUES ('%s', '%s', '', %i, %i, %s, %i, '%s', '%s',"
3461                    "         '%s', '%s', '%s', 0, %i)"
3462                    " ON CONFLICT (uuid) DO UPDATE"
3463                    " SET name = EXCLUDED.name,"
3464                    "     comment = EXCLUDED.comment,"
3465                    "     creation_time = EXCLUDED.creation_time,"
3466                    "     modification_time = EXCLUDED.modification_time,"
3467                    "     version = EXCLUDED.version,"
3468                    "     deprecated = EXCLUDED.deprecated,"
3469                    "     def_class = EXCLUDED.def_class,"
3470                    "     title = EXCLUDED.title,"
3471                    "     description = EXCLUDED.description,"
3472                    "     xml_file = EXCLUDED.xml_file,"
3473                    "     status = EXCLUDED.status,"
3474                    "     severity = 0,"
3475                    "     cve_refs = EXCLUDED.cve_refs;",
3476                    quoted_id,
3477                    quoted_oval_id,
3478                    definition_date_oldest == 0
3479                     ? file_timestamp
3480                     : definition_date_newest,
3481                    definition_date_oldest == 0
3482                     ? file_timestamp
3483                     : definition_date_oldest,
3484                    version,
3485                    (deprecated && strcasecmp (deprecated, "TRUE")) ? 1 : 0,
3486                    quoted_class,
3487                    quoted_title,
3488                    quoted_description,
3489                    quoted_xml_basename,
3490                    quoted_status,
3491                    cve_count);
3492               increment_transaction_size (&transaction_size);
3493               g_free (quoted_id);
3494               g_free (quoted_class);
3495               g_free (quoted_title);
3496               g_free (quoted_description);
3497               g_free (quoted_status);
3498               g_free (deprecated);
3499               g_free (version);
3500 
3501               reference = element_first_child (metadata);
3502               while (reference)
3503                 {
3504                   if (strcmp (element_name (reference), "reference") == 0)
3505                     {
3506                       gchar *source;
3507 
3508                       source = element_attribute (reference, "source");
3509                       if (source && strcasecmp (source, "cve") == 0)
3510                         {
3511                           gchar *ref_id, *quoted_ref_id;
3512 
3513                           ref_id = element_attribute (reference, "ref_id");
3514                           quoted_ref_id = sql_quote (ref_id);
3515                           g_free (ref_id);
3516 
3517                           sql ("INSERT INTO scap2.affected_ovaldefs (cve, ovaldef)"
3518                                " SELECT cves.id, ovaldefs.id"
3519                                " FROM scap2.cves, scap2.ovaldefs"
3520                                " WHERE cves.name='%s'"
3521                                " AND ovaldefs.name = '%s'"
3522                                " AND NOT EXISTS (SELECT * FROM scap2.affected_ovaldefs"
3523                                "                 WHERE cve = cves.id"
3524                                "                 AND ovaldef = ovaldefs.id);",
3525                                quoted_ref_id,
3526                                quoted_oval_id);
3527 
3528                           g_free (quoted_ref_id);
3529                           increment_transaction_size (&transaction_size);
3530                         }
3531                       g_free (source);
3532                     }
3533                   reference = element_next (reference);
3534                 }
3535 
3536               g_free (quoted_oval_id);
3537             }
3538           definition = element_next (definition);
3539         }
3540       child = element_next (child);
3541     }
3542 
3543   /* Cleanup. */
3544 
3545   g_free (quoted_xml_basename);
3546   element_free (element);
3547   sql_commit ();
3548   return 0;
3549 
3550  fail:
3551   g_free (quoted_xml_basename);
3552   g_warning ("Update of OVAL definitions failed at file '%s'",
3553              xml_path);
3554   sql_commit ();
3555   return -1;
3556 }
3557 
3558 /**
3559  * @brief Extract generator timestamp from OVAL element.
3560  *
3561  * @param[in]  element   OVAL element.
3562  *
3563  * @return Freshly allocated timestamp if found, else NULL.
3564  */
3565 static gchar *
oval_generator_timestamp(element_t element)3566 oval_generator_timestamp (element_t element)
3567 {
3568   gchar *generator_name;
3569   element_t generator;
3570 
3571   generator_name = g_strdup ("generator");
3572   generator = element_child (element, generator_name);
3573   g_free (generator_name);
3574   if (generator)
3575     {
3576       element_t timestamp;
3577       timestamp = element_child (generator, "oval:timestamp");
3578       if (timestamp)
3579         return element_text (timestamp);
3580     }
3581 
3582   return NULL;
3583 }
3584 
3585 /**
3586  * @brief Extract timestamp from OVAL XML.
3587  *
3588  * @param[in]  xml  OVAL XML.
3589  *
3590  * @return Freshly allocated timestamp, else NULL.
3591  */
3592 static gchar *
oval_timestamp(const gchar * xml)3593 oval_timestamp (const gchar *xml)
3594 {
3595   element_t element;
3596 
3597   if (parse_element (xml, &element))
3598     {
3599       g_warning ("%s: Failed to parse element: %s", __func__, xml);
3600       return NULL;
3601     }
3602 
3603   if (strcmp (element_name (element), "oval_definitions") == 0)
3604     {
3605       gchar *timestamp;
3606 
3607       timestamp = oval_generator_timestamp (element);
3608       if (timestamp)
3609         {
3610           element_free (element);
3611           return timestamp;
3612         }
3613     }
3614 
3615   if (strcmp (element_name (element), "oval_variables") == 0)
3616     {
3617       gchar *timestamp;
3618 
3619       timestamp = oval_generator_timestamp (element);
3620       if (timestamp)
3621         {
3622           element_free (element);
3623           return timestamp;
3624         }
3625     }
3626 
3627   if (strcmp (element_name (element), "oval_system_characteristics")
3628       == 0)
3629     {
3630       gchar *timestamp;
3631 
3632       timestamp = oval_generator_timestamp (element);
3633       if (timestamp)
3634         {
3635           element_free (element);
3636           return timestamp;
3637         }
3638     }
3639 
3640   g_warning ("%s: No timestamp: %s", __func__, xml);
3641   return NULL;
3642 }
3643 
3644 /**
3645  * @brief Files for update_scap_ovaldefs.
3646  */
3647 static array_t *oval_files = NULL;
3648 
3649 /**
3650  * @brief Add an OVAL file to oval_files.
3651  *
3652  * @param[in]  path       Path of file.
3653  * @param[in]  stat       Status of file.
3654  * @param[in]  flag       Dummy arg for nftw.
3655  * @param[in]  traversal  Dummy arg for nftw.
3656  *
3657  * @return 0 success, -1 error.
3658  */
3659 static int
oval_files_add(const char * path,const struct stat * stat,int flag,struct FTW * traversal)3660 oval_files_add (const char *path, const struct stat *stat, int flag,
3661                 struct FTW *traversal)
3662 {
3663   GError *error;
3664   gchar **pair, *oval_xml, *timestamp;
3665   gsize len;
3666   const char *dot;
3667 
3668   if (gvm_file_check_is_dir (path))
3669     return 0;
3670 
3671   dot = rindex (path, '.');
3672   if ((dot == NULL) || strcasecmp (dot, ".xml"))
3673     return 0;
3674 
3675   g_debug ("%s: path: %s", __func__, path);
3676 
3677   error = NULL;
3678   g_file_get_contents (path, &oval_xml, &len, &error);
3679   if (error)
3680     {
3681       g_warning ("%s: Failed get contents of %s: %s",
3682                  __func__,
3683                  path,
3684                  error->message);
3685       g_error_free (error);
3686       return -1;
3687     }
3688 
3689   /* Parse timestamp. */
3690 
3691   timestamp = oval_timestamp (oval_xml);
3692   g_free (oval_xml);
3693 
3694   /* Add file-timestamp pair to OVAL files. */
3695 
3696   pair = g_malloc (sizeof (gchar*) * 2);
3697   pair[0] = g_strdup (path);
3698   pair[1] = timestamp;
3699 
3700   array_add (oval_files, pair);
3701 
3702   return 0;
3703 }
3704 
3705 /**
3706  * @brief Compare OVAL files.
3707  *
3708  * @param[in]  one  First file.
3709  * @param[in]  two  Second file.
3710  *
3711  * @return 0 same, 1 one is greater than two, -1 two is greater than one.
3712  */
3713 static gint
oval_files_compare(gconstpointer one,gconstpointer two)3714 oval_files_compare (gconstpointer one, gconstpointer two)
3715 {
3716   gchar **file_info_one, **file_info_two;
3717 
3718   file_info_one = *((gchar***) one);
3719   file_info_two = *((gchar***) two);
3720 
3721   if (file_info_one[1] == NULL)
3722     {
3723       if (file_info_two[1] == NULL)
3724         return 0;
3725       return -1;
3726     }
3727 
3728   if (file_info_two[1] == NULL)
3729     return 1;
3730 
3731   return strcmp (file_info_one[1], file_info_two[1]);
3732 }
3733 
3734 
3735 /**
3736  * @brief Free oval_files.
3737  */
3738 static void
oval_files_free()3739 oval_files_free ()
3740 {
3741   int index;
3742 
3743   index = 0;
3744   while (oval_files && index < oval_files->len)
3745     {
3746       gchar **pair;
3747 
3748       pair = g_ptr_array_index (oval_files, index);
3749       g_free (pair[0]);
3750       g_free (pair[1]);
3751       index++;
3752     }
3753   array_free (oval_files);
3754   oval_files = NULL;
3755 }
3756 
3757 /**
3758  * @brief Update SCAP OVALDEFs.
3759  *
3760  * Assume that the databases are attached.
3761  *
3762  * @param[in]  private           Whether to update private SCAP data, instead
3763  *                               of the feed data.
3764  *
3765  * @return 0 success, -1 error.
3766  */
3767 static int
update_scap_ovaldefs(int private)3768 update_scap_ovaldefs (int private)
3769 {
3770   int count;
3771   gchar *oval_dir;
3772   guint index;
3773   struct stat state;
3774 
3775   assert (oval_files == NULL);
3776 
3777   if (private)
3778     g_info ("Updating user OVAL definitions.");
3779   else
3780     g_info ("Updating OVAL data");
3781 
3782   /* Get a list of the OVAL files. */
3783 
3784   if (private)
3785     {
3786       const char *subdir;
3787 
3788       subdir = getenv ("PRIVATE_SUBDIR");
3789       if ((subdir == NULL) || (strlen (subdir) == 0))
3790         subdir = "private";
3791 
3792       oval_dir = g_build_filename (GVM_SCAP_DATA_DIR, subdir, "oval",
3793                                    NULL);
3794     }
3795   else
3796     oval_dir = g_build_filename (GVM_SCAP_DATA_DIR, "oval", NULL);
3797 
3798   g_debug ("%s: private: %i", __func__, private);
3799   g_debug ("%s: oval_dir: %s", __func__, oval_dir);
3800 
3801   /* Pairs of pointers, pair[0]: absolute pathname, pair[1]: oval timestamp. */
3802   oval_files = make_array ();
3803 
3804   if (g_lstat (oval_dir, &state))
3805     {
3806       if (errno == ENOENT)
3807         {
3808           if (private)
3809             g_debug ("%s: no private OVAL dir (%s)",
3810                      __func__,
3811                      oval_dir);
3812           else
3813             g_warning ("%s: no OVAL dir (%s)",
3814                        __func__,
3815                        oval_dir);
3816           g_free (oval_dir);
3817           oval_files_free ();
3818           return 0;
3819         }
3820       g_warning ("%s: failed to lstat '%s': %s",
3821                   __func__,
3822                  oval_dir,
3823                  strerror (errno));
3824       g_free (oval_dir);
3825       oval_files_free ();
3826       return -1;
3827     }
3828 
3829   if (nftw (oval_dir, oval_files_add, 20, 0) == -1)
3830     {
3831       oval_files_free ();
3832       if (errno == ENOENT)
3833         {
3834           if (private)
3835             g_debug ("%s: nftw of private '%s': %s",
3836                      __func__,
3837                      oval_dir,
3838                      strerror (errno));
3839           else
3840             g_warning ("%s: nftw of '%s': %s",
3841                       __func__,
3842                       oval_dir,
3843                       strerror (errno));
3844           g_free (oval_dir);
3845           oval_files_free ();
3846           return 0;
3847         }
3848       g_warning ("%s: failed to traverse '%s': %s",
3849                   __func__,
3850                  oval_dir,
3851                  strerror (errno));
3852       g_free (oval_dir);
3853       oval_files_free ();
3854       return -1;
3855     }
3856 
3857   /* Sort the list by the OVAL timestamp. */
3858 
3859   g_ptr_array_sort (oval_files, oval_files_compare);
3860 
3861   if (private)
3862     {
3863       GError *error;
3864       GDir *directory;
3865       const gchar *entry;
3866 
3867       /* Check for files that aren't .xml or .asc. */
3868 
3869       error = NULL;
3870       directory = g_dir_open (oval_dir, 0, &error);
3871 
3872       if (directory == NULL)
3873         {
3874           assert (error);
3875 
3876           if (g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT))
3877             {
3878               g_warning ("No user data directory '%s' found.", oval_dir);
3879               g_error_free (error);
3880             }
3881           else
3882             {
3883               g_warning ("g_dir_open (%s) failed - %s", oval_dir,
3884                          error->message);
3885               g_free (oval_dir);
3886               g_error_free (error);
3887               oval_files_free ();
3888               return -1;
3889             }
3890         }
3891 
3892       entry = NULL;
3893       while ((entry = g_dir_read_name (directory)) != NULL)
3894         {
3895           if (g_str_has_suffix (entry, ".xml") < 0)
3896             continue;
3897           if (g_str_has_suffix (entry, ".asc") < 0)
3898             continue;
3899           g_warning ("Found non-XML and non-signature file '%s'.", entry);
3900         }
3901       g_dir_close (directory);
3902     }
3903 
3904   /* Process each file in the list, in the sorted order. */
3905 
3906   count = 0;
3907   for (index = 0; index < oval_files->len; index++)
3908     {
3909       gchar **pair;
3910 
3911       pair = g_ptr_array_index (oval_files, index);
3912       if (update_ovaldef_xml (pair, private))
3913         {
3914           oval_files_free ();
3915           g_free (oval_dir);
3916           return -1;
3917         }
3918       count++;
3919     }
3920 
3921   if (count == 0)
3922     g_warning ("%s: No XML files found in %s", __func__, oval_dir);
3923 
3924   if (private)
3925     {
3926       GString *oval_files_clause;
3927       int first;
3928       iterator_t files;
3929 
3930       /* Clean up user data. */
3931 
3932       g_info ("Cleaning up user OVAL data");
3933 
3934       g_debug ("%s: GVM_SCAP_DATA_DIR: %s", __func__, GVM_SCAP_DATA_DIR);
3935 
3936       oval_files_clause = g_string_new (" AND (xml_file NOT IN (");
3937       first = 1;
3938       for (index = 0; index < oval_files->len; index++)
3939         {
3940           gchar **pair;
3941           char *suffix;
3942 
3943           pair = g_ptr_array_index (oval_files, index);
3944           g_debug ("%s: pair[0]: %s", __func__, pair[0]);
3945           suffix = strstr (pair[0], GVM_SCAP_DATA_DIR);
3946           if (suffix == NULL)
3947             {
3948               g_warning ("%s: pair[0] missing GVM_SCAP_DATA_DIR: %s",
3949                          __func__,
3950                          pair[0]);
3951               g_free (oval_dir);
3952               oval_files_free ();
3953               return -1;
3954             }
3955           suffix += strlen (GVM_SCAP_DATA_DIR);
3956           g_string_append_printf (oval_files_clause,
3957                                   "%s'%s'",
3958                                   first ? "" : ", ",
3959                                   suffix);
3960           first = 0;
3961         }
3962       g_string_append (oval_files_clause, "))");
3963 
3964       init_iterator (&files,
3965                      "SELECT DISTINCT xml_file FROM scap2.ovaldefs"
3966                      " WHERE (xml_file NOT LIKE 'oval/%%')"
3967                      "%s",
3968                      oval_files_clause->str);
3969       first = 1;
3970       while (next (&files))
3971         {
3972           if (first)
3973             g_info ("Removing definitions formerly inserted from:");
3974           g_info ("%s", iterator_string (&files, 0));
3975           first = 0;
3976         }
3977       cleanup_iterator (&files);
3978 
3979       sql ("DELETE FROM scap2.ovaldefs"
3980            " WHERE (xml_file NOT LIKE 'oval/%%')"
3981            "%s;",
3982            oval_files_clause->str);
3983 
3984       g_string_free (oval_files_clause, TRUE);
3985     }
3986 
3987   /* Cleanup. */
3988 
3989   g_free (oval_dir);
3990   oval_files_free ();
3991   return 0;
3992 }
3993 
3994 
3995 /* CERT and SCAP update. */
3996 
3997 /**
3998  * @brief Reinit a db.
3999  *
4000  * @param[in]  name  Name of db.
4001  *
4002  * @return 0 success, -1 error.
4003  */
4004 static int
manage_db_reinit(const gchar * name)4005 manage_db_reinit (const gchar *name)
4006 {
4007   manage_db_remove (name);
4008   if (manage_db_init (name))
4009     {
4010       g_warning ("Could not reinitialize %s database", name);
4011       return -1;
4012     }
4013   return 0;
4014 }
4015 
4016 /**
4017  * @brief Sync a SecInfo DB.
4018  *
4019  * @param[in]  sigmask_current    Sigmask to restore in child.
4020  * @param[in]  update             Function to do the sync.
4021  * @param[in]  process_title      Process title.
4022  */
4023 static void
sync_secinfo(sigset_t * sigmask_current,int (* update)(void),const gchar * process_title)4024 sync_secinfo (sigset_t *sigmask_current, int (*update) (void),
4025               const gchar *process_title)
4026 {
4027   int pid;
4028 
4029   /* Fork a child to sync the db, so that the parent can return to the main
4030    * loop. */
4031 
4032   /* Use the default termination handlers for the child, because sync_secinfo
4033    * is called from the main process (via manage_schedule).  The signal
4034    * handlers inherited from the main process would not work because they
4035    * need the process to watch termination_signal. */
4036   pid = fork_with_handlers ();
4037   switch (pid)
4038     {
4039       case 0:
4040         /* Child.  Carry on to sync the db, reopen the database (required
4041          * after fork). */
4042 
4043         /* Restore the sigmask that was blanked for pselect in the parent. */
4044         pthread_sigmask (SIG_SETMASK, sigmask_current, NULL);
4045 
4046         /* Cleanup so that exit works. */
4047 
4048         cleanup_manage_process (FALSE);
4049 
4050         /* Init. */
4051 
4052         reinit_manage_process ();
4053         manage_session_init (current_credentials.uuid);
4054 
4055         break;
4056 
4057       case -1:
4058         /* Parent on error.  Reschedule and continue to next task. */
4059         g_warning ("%s: fork failed", __func__);
4060         return;
4061 
4062       default:
4063         /* Parent.  Continue to next task. */
4064         return;
4065 
4066     }
4067 
4068   proctitle_set (process_title);
4069 
4070   if (update () == 0)
4071     {
4072       check_alerts ();
4073     }
4074 
4075   exit (EXIT_SUCCESS);
4076 }
4077 
4078 /**
4079  * @brief Get the feed timestamp.
4080  *
4081  * @param[in]  name  Feed type: SCAP or CERT.
4082  *
4083  * @return Timestamp from feed.  0 if missing.  -1 on error.
4084  */
4085 static int
manage_feed_timestamp(const gchar * name)4086 manage_feed_timestamp (const gchar *name)
4087 {
4088   GError *error;
4089   gchar *timestamp;
4090   gsize len;
4091   time_t stamp;
4092 
4093   error = NULL;
4094   if (strcasecmp (name, "scap") == 0)
4095     g_file_get_contents (GVM_SCAP_DATA_DIR "/timestamp", &timestamp, &len,
4096                          &error);
4097   else
4098     g_file_get_contents (GVM_CERT_DATA_DIR "/timestamp", &timestamp, &len,
4099                          &error);
4100   if (error)
4101     {
4102       if (error->code == G_FILE_ERROR_NOENT)
4103         stamp = 0;
4104       else
4105         {
4106           g_warning ("%s: Failed to get %s feed timestamp: %s",
4107                      __func__,
4108                      name,
4109                      error->message);
4110           return -1;
4111         }
4112     }
4113   else
4114     {
4115       if (strlen (timestamp) < 8)
4116         {
4117           g_warning ("%s: %s feed timestamp too short: %s",
4118                      __func__,
4119                      name,
4120                      timestamp);
4121           g_free (timestamp);
4122           return -1;
4123         }
4124 
4125       timestamp[8] = '\0';
4126       stamp = parse_feed_timestamp (timestamp);
4127       g_free (timestamp);
4128       if (stamp == 0)
4129         return -1;
4130     }
4131 
4132  return stamp;
4133 }
4134 
4135 /**
4136  * @brief Gets the SCAP or CERT database version status.
4137  *
4138  * @param[in]  feed_type  The feed type to check. Must be "cert" or "scap".
4139  *
4140  * @return 0 feed current, 1 update needed, 2 database missing,
4141  *         3 missing "last_update", 4 inconsistent data, -1 error.
4142  */
4143 int
secinfo_feed_version_status(const char * feed_type)4144 secinfo_feed_version_status (const char *feed_type)
4145 {
4146   int last_feed_update, last_db_update;
4147 
4148   if (strcmp (feed_type, "cert") == 0)
4149     {
4150       if (manage_cert_loaded () == 0)
4151         return 2;
4152     }
4153   else if (strcmp (feed_type, "scap") == 0)
4154     {
4155       if (manage_scap_loaded () == 0)
4156         return 2;
4157     }
4158   else
4159     {
4160       g_warning ("%s: Unexpected feed type: %s", __func__, feed_type);
4161       return -1;
4162     }
4163 
4164   last_feed_update = manage_feed_timestamp (feed_type);
4165   if (last_feed_update == -1)
4166     return -1;
4167 
4168   last_db_update = sql_int ("SELECT coalesce ((SELECT value FROM %s.meta"
4169                             "                  WHERE name = 'last_update'),"
4170                             "                 '-3');",
4171                             feed_type);
4172   if (last_db_update == -3)
4173     return 3;
4174   else if (last_db_update < 0)
4175     return 4;
4176   else
4177     {
4178       if (last_db_update == last_feed_update)
4179         {
4180           return 0;
4181         }
4182 
4183       if (last_db_update > last_feed_update)
4184         {
4185           g_warning ("%s: last %s database update later than last feed update",
4186                      __func__, feed_type);
4187           return -1;
4188         }
4189     }
4190   return 1;
4191 }
4192 
4193 
4194 /* CERT update. */
4195 
4196 /**
4197  * @brief Ensure CERT db is at the right version, and in the right mode.
4198  *
4199  * @return 0 success, -1 error.
4200  */
4201 int
check_cert_db_version()4202 check_cert_db_version ()
4203 {
4204   int db_version = manage_cert_db_version ();
4205 
4206   if (db_version < GVMD_CERT_DATABASE_VERSION)
4207     {
4208       int ret;
4209       g_info ("Reinitialization of the CERT database necessary");
4210 
4211       ret = manage_db_reinit ("cert");
4212       if (ret)
4213         return ret;
4214 
4215       return sync_cert ();
4216     }
4217   else if (db_version > GVMD_CERT_DATABASE_VERSION)
4218     {
4219       g_warning ("%s: CERT database version %d is newer than"
4220                  " supported version %d",
4221                  __func__, db_version, GVMD_CERT_DATABASE_VERSION);
4222     }
4223   return 0;
4224 }
4225 
4226 /**
4227  * @brief Update timestamp in CERT db from feed timestamp.
4228  *
4229  * @return 0 success, -1 error.
4230  */
4231 static int
update_cert_timestamp()4232 update_cert_timestamp ()
4233 {
4234   GError *error;
4235   gchar *timestamp;
4236   gsize len;
4237   time_t stamp;
4238 
4239   error = NULL;
4240   g_file_get_contents (GVM_CERT_DATA_DIR "/timestamp", &timestamp, &len,
4241                        &error);
4242   if (error)
4243     {
4244       if (error->code == G_FILE_ERROR_NOENT)
4245         stamp = 0;
4246       else
4247         {
4248           g_warning ("%s: Failed to get timestamp: %s",
4249                      __func__,
4250                      error->message);
4251           return -1;
4252         }
4253     }
4254   else
4255     {
4256       if (strlen (timestamp) < 8)
4257         {
4258           g_warning ("%s: Feed timestamp too short: %s",
4259                      __func__,
4260                      timestamp);
4261           g_free (timestamp);
4262           return -1;
4263         }
4264 
4265       timestamp[8] = '\0';
4266       g_debug ("%s: parsing: %s", __func__, timestamp);
4267       stamp = parse_feed_timestamp (timestamp);
4268       g_free (timestamp);
4269       if (stamp == 0)
4270         return -1;
4271     }
4272 
4273   g_debug ("%s: setting last_update: %lld", __func__, (long long) stamp);
4274   sql ("UPDATE cert.meta SET value = '%lld' WHERE name = 'last_update';",
4275        (long long) stamp);
4276 
4277   return 0;
4278 }
4279 
4280 /**
4281  * @brief Update DFN-CERT Max CVSS.
4282  *
4283  * @param[in]  updated_dfn_cert  Whether CERT-Bund updated.
4284  * @param[in]  last_cert_update  Time of last CERT update.
4285  * @param[in]  last_scap_update  Time of last SCAP update.
4286  */
4287 static void
update_cvss_dfn_cert(int updated_dfn_cert,int last_cert_update,int last_scap_update)4288 update_cvss_dfn_cert (int updated_dfn_cert, int last_cert_update,
4289                       int last_scap_update)
4290 {
4291   /* TODO greenbone-certdata-sync did retries. */
4292 
4293   if (updated_dfn_cert || (last_scap_update > last_cert_update))
4294     {
4295       g_info ("Updating Max CVSS for DFN-CERT");
4296       sql ("UPDATE cert.dfn_cert_advs"
4297            " SET severity = (SELECT max (severity)"
4298            "                  FROM scap.cves"
4299            "                  WHERE name"
4300            "                  IN (SELECT cve_name"
4301            "                      FROM cert.dfn_cert_cves"
4302            "                      WHERE adv_id = dfn_cert_advs.id)"
4303            "                  AND severity != 0);");
4304 
4305       g_info ("Updating DFN-CERT CVSS max succeeded.");
4306     }
4307   else
4308     g_info ("Updating DFN-CERT CVSS max succeeded (nothing to do).");
4309 }
4310 
4311 /**
4312  * @brief Update CERT-Bund Max CVSS.
4313  *
4314  * @param[in]  updated_cert_bund  Whether CERT-Bund updated.
4315  * @param[in]  last_cert_update  Time of last CERT update.
4316  * @param[in]  last_scap_update  Time of last SCAP update.
4317  */
4318 static void
update_cvss_cert_bund(int updated_cert_bund,int last_cert_update,int last_scap_update)4319 update_cvss_cert_bund (int updated_cert_bund, int last_cert_update,
4320                        int last_scap_update)
4321 {
4322   /* TODO greenbone-certdata-sync did retries. */
4323 
4324   if (updated_cert_bund || (last_scap_update > last_cert_update))
4325     {
4326       g_info ("Updating Max CVSS for CERT-Bund");
4327       sql ("UPDATE cert.cert_bund_advs"
4328            " SET severity = (SELECT max (severity)"
4329            "                  FROM scap.cves"
4330            "                  WHERE name"
4331            "                     IN (SELECT cve_name"
4332            "                         FROM cert.cert_bund_cves"
4333            "                         WHERE adv_id = cert_bund_advs.id)"
4334            "                  AND severity != 0);");
4335 
4336       g_info ("Updating CERT-Bund CVSS max succeeded.");
4337     }
4338   else
4339     g_info ("Updating CERT-Bund CVSS max succeeded (nothing to do).");
4340 }
4341 
4342 /**
4343  * @brief Sync the CERT DB.
4344  *
4345  * @return 0 success, -1 error.
4346  */
4347 static int
sync_cert()4348 sync_cert ()
4349 {
4350   int scap_db_version;
4351   int last_feed_update, last_cert_update, updated_dfn_cert;
4352   int updated_cert_bund;
4353 
4354   if (manage_cert_db_exists ())
4355     {
4356       if (check_cert_db_version ())
4357         return -1;
4358     }
4359   else
4360     {
4361       g_info ("Initializing CERT database");
4362       if (manage_db_init ("cert"))
4363         {
4364           g_warning ("%s: Could not initialize CERT database", __func__);
4365           return -1;
4366         }
4367     }
4368 
4369   last_cert_update = 0;
4370   if (manage_cert_loaded ())
4371     last_cert_update = sql_int ("SELECT coalesce ((SELECT value FROM cert.meta"
4372                                 "                  WHERE name = 'last_update'),"
4373                                 "                 '-1');");
4374 
4375   if (last_cert_update == -1)
4376     {
4377       g_warning ("%s: Inconsistent data. Resetting CERT database.",
4378                  __func__);
4379       if (manage_db_reinit ("cert"))
4380         {
4381           g_warning ("%s: could not reinitialize CERT database", __func__);
4382           return -1;
4383         }
4384       last_cert_update = 0;
4385     }
4386 
4387   last_feed_update = manage_feed_timestamp ("cert");
4388   if (last_feed_update == -1)
4389     return -1;
4390 
4391   if (last_cert_update >= last_feed_update)
4392     return -1;
4393 
4394   g_debug ("%s: sync", __func__);
4395 
4396   g_info ("%s: Updating data from feed", __func__);
4397 
4398   g_debug ("%s: update dfn", __func__);
4399 
4400   updated_dfn_cert = update_dfn_cert_advisories (last_cert_update);
4401   if (updated_dfn_cert == -1)
4402     goto fail;
4403 
4404   g_debug ("%s: update bund", __func__);
4405 
4406   updated_cert_bund = update_cert_bund_advisories (last_cert_update);
4407   if (updated_cert_bund == -1)
4408     goto fail;
4409 
4410   g_debug ("%s: update cvss", __func__);
4411 
4412   /* Update CERT data that depends on SCAP. */
4413   scap_db_version = manage_scap_db_version();
4414 
4415   if (scap_db_version == -1)
4416     g_info ("SCAP database does not exist (yet),"
4417             " skipping CERT severity score update");
4418   else if (scap_db_version < GVMD_SCAP_DATABASE_VERSION)
4419     g_info ("SCAP database has to be migrated,"
4420             " skipping CERT severity score update");
4421   else if (scap_db_version > GVMD_SCAP_DATABASE_VERSION)
4422     g_warning ("SCAP database is newer than supported version,"
4423                " skipping CERT severity score update");
4424   else
4425     {
4426       int last_scap_update;
4427 
4428       last_scap_update
4429         = sql_int ("SELECT coalesce ((SELECT value FROM scap.meta"
4430                    "                  WHERE name = 'last_update'),"
4431                    "                 '0');");
4432       g_debug ("%s: last_scap_update: %i", __func__, last_scap_update);
4433       g_debug ("%s: last_cert_update: %i", __func__, last_cert_update);
4434 
4435       update_cvss_dfn_cert (updated_dfn_cert,
4436                             last_cert_update,
4437                             last_scap_update);
4438       update_cvss_cert_bund (updated_cert_bund,
4439                              last_cert_update,
4440                              last_scap_update);
4441     }
4442 
4443   g_debug ("%s: update timestamp", __func__);
4444 
4445   if (update_cert_timestamp ())
4446     goto fail;
4447 
4448   g_info ("%s: Updating CERT info succeeded.", __func__);
4449 
4450   return 0;
4451 
4452  fail:
4453   return -1;
4454 }
4455 
4456 /**
4457  * @brief Sync the CERT DB.
4458  *
4459  * @param[in]  sigmask_current  Sigmask to restore in child.
4460  */
4461 void
manage_sync_cert(sigset_t * sigmask_current)4462 manage_sync_cert (sigset_t *sigmask_current)
4463 {
4464   sync_secinfo (sigmask_current,
4465                 sync_cert,
4466                 "gvmd: Syncing CERT");
4467 }
4468 
4469 
4470 /* SCAP update. */
4471 
4472 /**
4473  * @brief Ensure SCAP db is at the right version, and in the right mode.
4474  *
4475  * @return 0 success, -1 error.
4476  */
4477 int
check_scap_db_version()4478 check_scap_db_version ()
4479 {
4480   int db_version = manage_scap_db_version ();
4481 
4482   if (db_version < GVMD_SCAP_DATABASE_VERSION)
4483     {
4484       g_info ("Reinitialization of the SCAP database necessary");
4485       manage_db_remove ("scap");
4486       return update_scap (TRUE);
4487     }
4488   else if (db_version > GVMD_SCAP_DATABASE_VERSION)
4489     {
4490       g_warning ("%s: SCAP database version %d is newer than"
4491                  " supported version %d",
4492                  __func__, db_version, GVMD_SCAP_DATABASE_VERSION);
4493     }
4494   return 0;
4495 }
4496 
4497 /**
4498  * @brief Update timestamp in SCAP db from feed timestamp.
4499  *
4500  * @return 0 success, -1 error.
4501  */
4502 static int
update_scap_timestamp()4503 update_scap_timestamp ()
4504 {
4505   GError *error;
4506   gchar *timestamp;
4507   gsize len;
4508   time_t stamp;
4509 
4510   error = NULL;
4511   g_file_get_contents (GVM_SCAP_DATA_DIR "/timestamp", &timestamp, &len,
4512                        &error);
4513   if (error)
4514     {
4515       if (error->code == G_FILE_ERROR_NOENT)
4516         stamp = 0;
4517       else
4518         {
4519           g_warning ("%s: Failed to get timestamp: %s",
4520                      __func__,
4521                      error->message);
4522           return -1;
4523         }
4524     }
4525   else
4526     {
4527       if (strlen (timestamp) < 8)
4528         {
4529           g_warning ("%s: Feed timestamp too short: %s",
4530                      __func__,
4531                      timestamp);
4532           g_free (timestamp);
4533           return -1;
4534         }
4535 
4536       timestamp[8] = '\0';
4537       g_debug ("%s: parsing: %s", __func__, timestamp);
4538       stamp = parse_feed_timestamp (timestamp);
4539       g_free (timestamp);
4540       if (stamp == 0)
4541         return -1;
4542     }
4543 
4544   g_debug ("%s: setting last_update: %lld", __func__, (long long) stamp);
4545   sql ("UPDATE scap2.meta SET value = '%lld' WHERE name = 'last_update';",
4546        (long long) stamp);
4547 
4548   return 0;
4549 }
4550 
4551 /**
4552  * @brief Update SCAP Max CVSS.
4553  */
4554 static void
update_scap_cvss()4555 update_scap_cvss ()
4556 {
4557   /* TODO greenbone-scapdata-sync did retries. */
4558 
4559   g_info ("Updating CVSS scores and CVE counts for CPEs");
4560   sql ("UPDATE scap2.cpes"
4561        " SET (severity, cve_refs)"
4562        "       = (WITH affected_cves"
4563        "          AS (SELECT cve FROM scap2.affected_products"
4564        "              WHERE cpe=cpes.id)"
4565        "          SELECT (SELECT max (severity) FROM scap2.cves"
4566        "                  WHERE id IN (SELECT cve FROM affected_cves)),"
4567        "                 (SELECT count (*) FROM affected_cves));");
4568 
4569   g_info ("Updating CVSS scores for OVAL definitions");
4570   sql ("UPDATE scap2.ovaldefs"
4571        " SET severity = (SELECT max (severity)"
4572        "                 FROM scap2.cves"
4573        "                 WHERE id IN (SELECT cve"
4574        "                              FROM scap2.affected_ovaldefs"
4575        "                              WHERE ovaldef=ovaldefs.id)"
4576        "                 AND severity != 0);");
4577 }
4578 
4579 /**
4580  * @brief Update SCAP placeholder CVES.
4581  */
4582 static void
update_scap_placeholders()4583 update_scap_placeholders ()
4584 {
4585   /* TODO greenbone-scapdata-sync did retries. */
4586 
4587   g_info ("Updating placeholder CPEs");
4588   sql ("UPDATE scap2.cpes"
4589        " SET creation_time = (SELECT min (creation_time)"
4590        "                      FROM scap2.cves"
4591        "                      WHERE id IN (SELECT cve"
4592        "                                   FROM scap2.affected_products"
4593        "                                   WHERE cpe=cpes.id)),"
4594        "     modification_time = (SELECT min(creation_time)"
4595        "                          FROM scap2.cves"
4596        "                          WHERE id IN (SELECT cve"
4597        "                                       FROM scap2.affected_products"
4598        "                                       WHERE cpe=cpes.id))"
4599        " WHERE cpes.title IS NULL;");
4600 }
4601 
4602 /**
4603  * @brief Finish scap update.
4604  *
4605  * @return 0 success, -1 error.
4606  */
4607 static int
update_scap_end()4608 update_scap_end ()
4609 {
4610   int cert_db_version;
4611 
4612   g_debug ("%s: update timestamp", __func__);
4613 
4614   if (update_scap_timestamp ())
4615     return -1;
4616 
4617   /* Replace the real scap schema with the new one. */
4618 
4619   if (sql_int ("SELECT EXISTS (SELECT schema_name FROM"
4620                "               information_schema.schemata"
4621                "               WHERE schema_name = 'scap');"))
4622     {
4623       sql ("ALTER SCHEMA scap RENAME TO scap3;");
4624       sql ("ALTER SCHEMA scap2 RENAME TO scap;");
4625       sql ("DROP SCHEMA scap3 CASCADE;");
4626       /* View 'vulns' contains references into the SCAP schema, so it is
4627        * removed by the CASCADE. */
4628       create_view_vulns ();
4629     }
4630   else
4631     sql ("ALTER SCHEMA scap2 RENAME TO scap;");
4632 
4633   /* Update CERT data that depends on SCAP. */
4634   cert_db_version = manage_cert_db_version();
4635 
4636   if (cert_db_version == -1)
4637     g_info ("CERT database does not exist (yet),"
4638             " skipping CERT severity score update");
4639   else if (cert_db_version < GVMD_CERT_DATABASE_VERSION)
4640     g_info ("CERT database has to be migrated,"
4641             " skipping CERT severity score update");
4642   else if (cert_db_version > GVMD_CERT_DATABASE_VERSION)
4643     g_warning ("CERT database is newer than supported version,"
4644                " skipping CERT severity score update");
4645   else
4646     {
4647       int last_cert_update, last_scap_update;
4648 
4649       last_cert_update = sql_int ("SELECT"
4650                                   " coalesce ((SELECT value FROM cert.meta"
4651                                   "            WHERE name = 'last_update'),"
4652                                   "           '0');");
4653 
4654       last_scap_update = sql_int ("SELECT"
4655                                   " coalesce ((SELECT value FROM scap.meta"
4656                                   "            WHERE name = 'last_update'),"
4657                                   "           '0');");
4658 
4659       g_debug ("%s: last_scap_update: %i", __func__, last_scap_update);
4660       g_debug ("%s: last_cert_update: %i", __func__, last_cert_update);
4661 
4662       update_cvss_dfn_cert (1, last_cert_update, last_scap_update);
4663       update_cvss_cert_bund (1, last_cert_update, last_scap_update);
4664     }
4665 
4666   /* Analyze. */
4667 
4668   sql ("ANALYZE scap.cves;");
4669   sql ("ANALYZE scap.cpes;");
4670   sql ("ANALYZE scap.affected_products;");
4671   sql ("ANALYZE scap.ovaldefs;");
4672   sql ("ANALYZE scap.ovalfiles;");
4673   sql ("ANALYZE scap.affected_ovaldefs;");
4674 
4675   g_info ("%s: Updating SCAP info succeeded", __func__);
4676   proctitle_set ("gvmd: Syncing SCAP: done");
4677 
4678   return 0;
4679 }
4680 
4681 /**
4682  * @brief Try load the feed from feed CSV files.
4683  *
4684  * @return 0 success, -1 error, 1 no CSV.
4685  */
4686 static int
try_load_csv()4687 try_load_csv ()
4688 {
4689   gchar *file_cves, *file_cpes, *file_affected_products;
4690   gchar *file_ovaldefs, *file_ovalfiles, *file_affected_ovaldefs;
4691 
4692   file_cves = g_build_filename (GVM_SCAP_DATA_CSV_DIR, "table-cves.csv", NULL);
4693   file_cpes = g_build_filename (GVM_SCAP_DATA_CSV_DIR, "table-cpes.csv", NULL);
4694   file_affected_products = g_build_filename (GVM_SCAP_DATA_CSV_DIR,
4695                                              "table-affected-products.csv",
4696                                              NULL);
4697   file_ovaldefs = g_build_filename (GVM_SCAP_DATA_CSV_DIR,
4698                                     "table-ovaldefs.csv",
4699                                     NULL);
4700   file_ovalfiles = g_build_filename (GVM_SCAP_DATA_CSV_DIR,
4701                                      "table-ovalfiles.csv",
4702                                      NULL);
4703   file_affected_ovaldefs = g_build_filename (GVM_SCAP_DATA_CSV_DIR,
4704                                              "table-affected-ovaldefs.csv",
4705                                              NULL);
4706 
4707   if (gvm_file_is_readable (file_cves)
4708       && gvm_file_is_readable (file_cpes)
4709       && gvm_file_is_readable (file_affected_products)
4710       && gvm_file_is_readable (file_ovaldefs)
4711       && gvm_file_is_readable (file_ovalfiles)
4712       && gvm_file_is_readable (file_affected_ovaldefs))
4713     {
4714       /* Create a new schema, "scap2". */
4715 
4716       if (manage_db_init ("scap"))
4717         {
4718           g_warning ("%s: could not initialize SCAP database 2", __func__);
4719           return -1;
4720         }
4721 
4722       sql ("COPY scap2.cves FROM '%s' WITH (FORMAT csv);", file_cves);
4723       g_free (file_cves);
4724 
4725       sql ("COPY scap2.cpes FROM '%s' WITH (FORMAT csv);", file_cpes);
4726       g_free (file_cpes);
4727 
4728       sql ("COPY scap2.affected_products FROM '%s' WITH (FORMAT csv);",
4729            file_affected_products);
4730       g_free (file_affected_products);
4731 
4732       sql ("COPY scap2.ovaldefs FROM '%s' WITH (FORMAT csv);", file_ovaldefs);
4733       g_free (file_ovaldefs);
4734 
4735       sql ("COPY scap2.ovalfiles FROM '%s' WITH (FORMAT csv);", file_ovalfiles);
4736       g_free (file_ovalfiles);
4737 
4738       sql ("COPY scap2.affected_ovaldefs FROM '%s' WITH (FORMAT csv);",
4739            file_affected_ovaldefs);
4740       g_free (file_affected_ovaldefs);
4741 
4742       /* Add the indexes and constraints, now that the data is ready. */
4743 
4744       g_debug ("%s: add indexes", __func__);
4745       proctitle_set ("gvmd: Syncing SCAP: Adding indexes");
4746 
4747       if (manage_db_init_indexes ("scap"))
4748         {
4749           g_warning ("%s: could not initialize SCAP indexes", __func__);
4750           return -1;
4751         }
4752 
4753       g_debug ("%s: add constraints", __func__);
4754       proctitle_set ("gvmd: Syncing SCAP: Adding constraints");
4755 
4756       if (manage_db_add_constraints ("scap"))
4757         {
4758           g_warning ("%s: could not add SCAP constraints", __func__);
4759           return -1;
4760         }
4761 
4762       return update_scap_end ();
4763     }
4764   return 1;
4765 }
4766 
4767 /**
4768  * @brief Update all data in the SCAP DB.
4769  *
4770  * @param[in]  reset_scap_db  Whether to rebuild regardless of last_scap_update.
4771  *
4772  * @return 0 success, -1 error.
4773  */
4774 static int
update_scap(gboolean reset_scap_db)4775 update_scap (gboolean reset_scap_db)
4776 {
4777   if (reset_scap_db)
4778     g_warning ("%s: Full rebuild requested, resetting SCAP db",
4779                __func__);
4780   else if (manage_scap_loaded () == 0)
4781     g_warning ("%s: No SCAP db present, rebuilding SCAP db from scratch",
4782                __func__);
4783   else
4784     {
4785       int last_scap_update;
4786 
4787       last_scap_update = sql_int ("SELECT coalesce ((SELECT value FROM scap.meta"
4788                                   "                  WHERE name = 'last_update'),"
4789                                   "                 '-3');");
4790       if (last_scap_update == -3)
4791         g_warning ("%s: SCAP db missing last_update record, resetting SCAP db",
4792                    __func__);
4793       else if (last_scap_update < 0)
4794         g_warning ("%s: Inconsistent data, resetting SCAP db",
4795                    __func__);
4796       else
4797         {
4798           int last_feed_update;
4799 
4800           last_feed_update = manage_feed_timestamp ("scap");
4801 
4802           if (last_feed_update == -1)
4803             return -1;
4804 
4805           if (last_scap_update == last_feed_update)
4806             {
4807               proctitle_set ("gvmd: Syncing SCAP: done");
4808               return 0;
4809             }
4810 
4811           if (last_scap_update > last_feed_update)
4812             {
4813               g_warning ("%s: last scap update later than last feed update",
4814                          __func__);
4815               return -1;
4816             }
4817         }
4818     }
4819 
4820   /* If there's CSV in the feed, just load it. */
4821 
4822   if (try_load_csv () == 0)
4823     return 0;
4824 
4825   /* Create a new schema, "scap2". */
4826 
4827   if (manage_db_init ("scap"))
4828     {
4829       g_warning ("%s: could not initialize SCAP database 2", __func__);
4830       return -1;
4831     }
4832 
4833   /* Add the indexes and constraints. */
4834 
4835   g_debug ("%s: add indexes", __func__);
4836   proctitle_set ("gvmd: Syncing SCAP: Adding indexes");
4837 
4838   if (manage_db_init_indexes ("scap"))
4839     {
4840       g_warning ("%s: could not initialize SCAP indexes", __func__);
4841       return -1;
4842     }
4843 
4844   if (manage_db_add_constraints ("scap"))
4845     {
4846       g_warning ("%s: could not add SCAP constraints", __func__);
4847       return -1;
4848     }
4849 
4850   /* Update into the new schema. */
4851 
4852   g_debug ("%s: sync", __func__);
4853 
4854   g_info ("%s: Updating data from feed", __func__);
4855 
4856   g_debug ("%s: update cpes", __func__);
4857   proctitle_set ("gvmd: Syncing SCAP: Updating CPEs");
4858 
4859   if (update_scap_cpes () == -1)
4860     return -1;
4861 
4862   g_debug ("%s: update cves", __func__);
4863   proctitle_set ("gvmd: Syncing SCAP: Updating CVEs");
4864 
4865   if (update_scap_cves () == -1)
4866     return -1;
4867 
4868   g_debug ("%s: update ovaldefs", __func__);
4869   proctitle_set ("gvmd: Syncing SCAP: Updating OVALdefs");
4870 
4871   if (update_scap_ovaldefs (0 /* Feed data. */) == -1)
4872     return -1;
4873 
4874   g_debug ("%s: updating user defined data", __func__);
4875 
4876   if (update_scap_ovaldefs (1 /* Private data. */) == -1)
4877     return -1;
4878 
4879   /* Do calculations that need all data. */
4880 
4881   g_debug ("%s: update max cvss", __func__);
4882   proctitle_set ("gvmd: Syncing SCAP: Updating max CVSS");
4883 
4884   update_scap_cvss ();
4885 
4886   g_debug ("%s: update placeholders", __func__);
4887   proctitle_set ("gvmd: Syncing SCAP: Updating placeholders");
4888 
4889   update_scap_placeholders ();
4890 
4891   return update_scap_end ();
4892 }
4893 
4894 /**
4895  * @brief Sync the SCAP DB.
4896  *
4897  * @return 0 success, -1 error.
4898  */
4899 static int
sync_scap()4900 sync_scap ()
4901 {
4902   return update_scap (FALSE);
4903 }
4904 
4905 /**
4906  * @brief Sync the SCAP DB.
4907  *
4908  * @param[in]  sigmask_current  Sigmask to restore in child.
4909  */
4910 void
manage_sync_scap(sigset_t * sigmask_current)4911 manage_sync_scap (sigset_t *sigmask_current)
4912 {
4913   sync_secinfo (sigmask_current,
4914                 sync_scap,
4915                 "gvmd: Syncing SCAP");
4916 }
4917 
4918 /**
4919  * @brief Rebuild the entire SCAP DB.
4920  *
4921  * @return 0 success, 2 sync running, -1 error
4922  */
4923 static int
rebuild_scap()4924 rebuild_scap ()
4925 {
4926   int ret = -1;
4927   lockfile_t lockfile;
4928 
4929   ret = feed_lockfile_lock_timeout (&lockfile);
4930   if (ret == 1)
4931     return 2;
4932   else if (ret)
4933     return -1;
4934 
4935   ret = update_scap (TRUE);
4936   if (ret == 1)
4937     ret = 2;
4938 
4939   if (feed_lockfile_unlock (&lockfile))
4940     {
4941       g_warning (
4942         "%s: failed to close lock file: %s", __func__, strerror (errno));
4943       return -1;
4944     }
4945 
4946   return ret;
4947 }
4948 
4949 /**
4950  * @brief Rebuild part of the SCAP DB.
4951  *
4952  * @param[in]  log_config  Log configuration.
4953  * @param[in]  database    Location of manage database.
4954  *
4955  * @return 0 success, -1 error.
4956  */
4957 int
manage_rebuild_scap(GSList * log_config,const db_conn_info_t * database)4958 manage_rebuild_scap (GSList *log_config, const db_conn_info_t *database)
4959 {
4960   int ret;
4961 
4962   g_info ("   Rebuilding SCAP data");
4963 
4964   ret = manage_option_setup (log_config, database);
4965   if (ret)
4966     return -1;
4967 
4968   ret = rebuild_scap ();
4969   if (ret == 2)
4970     {
4971       printf ("SCAP sync is currently running.\n");
4972       goto fail;
4973     }
4974   else if (ret)
4975     goto fail;
4976 
4977   manage_option_cleanup ();
4978   return 0;
4979 
4980 fail:
4981   manage_option_cleanup ();
4982   return -1;
4983 }
4984 
4985 /**
4986  * @brief Set the SecInfo update commit size.
4987  *
4988  * @param new_commit_size The new SecInfo update commit size.
4989  */
4990 void
set_secinfo_commit_size(int new_commit_size)4991 set_secinfo_commit_size (int new_commit_size)
4992 {
4993   if (new_commit_size < 0)
4994     secinfo_commit_size = 0;
4995   else
4996     secinfo_commit_size = new_commit_size;
4997 }
4998