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", ×tamp, &len,
4096 &error);
4097 else
4098 g_file_get_contents (GVM_CERT_DATA_DIR "/timestamp", ×tamp, &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", ×tamp, &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", ×tamp, &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