]> git.ipfire.org Git - thirdparty/strongswan.git/commitdiff
oval-updater: Find vulnerabilities in Linux packages oval-updater
authorAndreas Steffen <andreas.steffen@strongswan.org>
Mon, 8 Oct 2018 08:48:00 +0000 (10:48 +0200)
committerAndreas Steffen <andreas.steffen@strongswan.org>
Tue, 30 Oct 2018 12:30:12 +0000 (13:30 +0100)
configure.ac
src/Makefile.am
src/oval-updater/.gitignore [new file with mode: 0644]
src/oval-updater/Makefile.am [new file with mode: 0644]
src/oval-updater/oval-updater.8 [new file with mode: 0644]
src/oval-updater/oval-updater.8.in [new file with mode: 0644]
src/oval-updater/oval-updater.c [new file with mode: 0644]
src/oval-updater/oval-updater.sh [new file with mode: 0755]
src/oval-updater/oval.c [new file with mode: 0644]
src/oval-updater/oval.h [new file with mode: 0644]

index 0ff2819c6795879ae52b47a0342f19d0f888dfd1..00e06c8f3b79409d5aa74e3f8c240e9523c569d0 100644 (file)
@@ -312,7 +312,7 @@ ARG_ENABL_SET([perl-cpan],      [enable build of provided perl CPAN module.])
 ARG_ENABL_SET([perl-cpan-install],[enable installation of provided CPAN module.])
 ARG_ENABL_SET([tss-trousers],   [enable the use of the TrouSerS Trusted Software Stack])
 ARG_ENABL_SET([tss-tss2],       [enable the use of the TSS 2.0 Trusted Software Stack])
-
+ARG_ENABL_SET([oval-updater],           [enable oval-updater])
 # compile options
 ARG_ENABL_SET([coverage],       [enable lcov coverage report generation.])
 ARG_ENABL_SET([leak-detective], [enable malloc hooks to find memory leaks.])
@@ -453,7 +453,7 @@ if test x$swanctl = xtrue; then
        vici=true
 fi
 
-if test x$smp = xtrue -o x$tnccs_11 = xtrue -o x$tnc_ifmap = xtrue; then
+if test x$smp = xtrue -o x$tnccs_11 = xtrue -o x$tnc_ifmap = xtrue -o oval-updater = xtrue; then
        xml=true
 fi
 
@@ -1741,6 +1741,7 @@ AM_CONDITIONAL(USE_RUBY_GEMS, test x$ruby_gems = xtrue)
 AM_CONDITIONAL(USE_PYTHON_EGGS, test x$python_eggs = xtrue)
 AM_CONDITIONAL(USE_PERL_CPAN, test x$perl_cpan = xtrue)
 AM_CONDITIONAL(USE_PY_TEST, test "x$PY_TEST" != x)
+AM_CONDITIONAL(USE_OVAL_UPDATER, test x$oval-updater = xtrue)
 
 # ========================
 #  set global definitions
@@ -2008,6 +2009,7 @@ AC_CONFIG_FILES([
        src/pt-tls-client/Makefile
        src/sw-collector/Makefile
        src/sec-updater/Makefile
+       src/oval-updater/Makefile
        src/swanctl/Makefile
        scripts/Makefile
        testing/Makefile
@@ -2043,6 +2045,7 @@ AC_CONFIG_FILES([
        src/pt-tls-client/pt-tls-client.1
        src/sw-collector/sw-collector.8
        src/sec-updater/sec-updater.8
+       src/oval-updater/oval-updater.8
 ])
 
 AC_OUTPUT
index 6eacbe293006fcfad33590f2e5015fb0e2078841..c583dee7efa92d5ad187ff0bcb6bafc71e50b436 100644 (file)
@@ -143,3 +143,7 @@ endif
 if USE_TPM
   SUBDIRS += tpm_extendpcr
 endif
+
+if USE_OVAL_UPDATER
+  SUBDIRS += oval-updater
+endif
diff --git a/src/oval-updater/.gitignore b/src/oval-updater/.gitignore
new file mode 100644 (file)
index 0000000..287b82b
--- /dev/null
@@ -0,0 +1 @@
+oval-updater
diff --git a/src/oval-updater/Makefile.am b/src/oval-updater/Makefile.am
new file mode 100644 (file)
index 0000000..522720b
--- /dev/null
@@ -0,0 +1,26 @@
+if !USE_WINDOWS
+
+sbin_PROGRAMS = oval-updater
+
+AM_CFLAGS = \
+       ${xml_CFLAGS}
+
+AM_CPPFLAGS = \
+       -I$(top_srcdir)/src/libstrongswan \
+       -DPLUGINS=\""sqlite"\"
+
+oval_updater_SOURCES = \
+       oval-updater.c oval.h oval.c
+
+oval_updater_LDADD = \
+       $(top_builddir)/src/libstrongswan/libstrongswan.la \
+       ${xml_LIBS}
+
+oval-updater.o : $(top_builddir)/config.status
+
+EXTRA_DIST = oval-updater.sh
+
+man8_MANS = oval-updater.8
+CLEANFILES = $(man8_MANS)
+
+endif
diff --git a/src/oval-updater/oval-updater.8 b/src/oval-updater/oval-updater.8
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/oval-updater/oval-updater.8.in b/src/oval-updater/oval-updater.8.in
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/oval-updater/oval-updater.c b/src/oval-updater/oval-updater.c
new file mode 100644 (file)
index 0000000..054f22e
--- /dev/null
@@ -0,0 +1,596 @@
+/*
+ * Copyright (C) 2018 Andreas Steffen
+ * HSR Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+#define _GNU_SOURCE
+#include <getopt.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <syslog.h>
+#include <stdlib.h>
+
+#include <library.h>
+#include <utils/debug.h>
+#include <collections/hashtable.h>
+#include <collections/array.h>
+
+#include <libxml/parser.h>
+
+#include "oval.h"
+
+/**
+ * global debug output variables
+ */
+static int debug_level = 1;
+static bool stderr_quiet = FALSE;
+
+/**
+ * oval_updater dbg function
+ */
+static void oval_updater_dbg(debug_t group, level_t level, char *fmt, ...)
+{
+       int priority = LOG_INFO;
+       char buffer[8192];
+       char *current = buffer, *next;
+       va_list args;
+
+       if (level <= debug_level)
+       {
+               if (!stderr_quiet)
+               {
+                       va_start(args, fmt);
+                       vfprintf(stderr, fmt, args);
+                       fprintf(stderr, "\n");
+                       va_end(args);
+               }
+
+               /* write in memory buffer first */
+               va_start(args, fmt);
+               vsnprintf(buffer, sizeof(buffer), fmt, args);
+               va_end(args);
+
+               /* do a syslog with every line */
+               while (current)
+               {
+                       next = strchr(current, '\n');
+                       if (next)
+                       {
+                               *(next++) = '\0';
+                       }
+                       syslog(priority, "%s\n", current);
+                       current = next;
+               }
+       }
+}
+
+/**
+ * atexit handler to close everything on shutdown
+ */
+static void cleanup(void)
+{
+       closelog();
+       library_deinit();
+}
+
+static void usage(void)
+{
+       printf("\
+Usage:\n\
+  oval-updater --help\n\
+  oval-updater [--debug <level>] [--quiet]  --os <string> --archs <string> \n\
+                --file <filename>\n\n\
+  Options:\n\
+    --help             print usage information\n\
+    --debug <level>    set debug level\n\
+    --quiet            suppress debug output to stderr\n\
+    --os <string>      operating system\n\
+    --archs <string>   space separated enumeration of architectures\n\
+    --file <filename>  oval definition file\n");
+ }
+
+/**
+ * global objects
+ */
+hashtable_t *tests, *objects, *states;
+database_t *db;
+
+static void extract_criteria(oval_t *oval, xmlNodePtr node)
+{
+       xmlNodePtr cur, c, tst, ste, s;
+
+       for (c = node->xmlChildrenNode; c != NULL; c = c->next)
+       {
+               /* ignore empty or blank nodes */
+               if (xmlIsBlankNode(c))
+               {
+                       continue;
+               }
+               if (!xmlStrcmp(c->name, "criterion"))
+               {
+                       enumerator_t *e;
+                       char *test_ref = NULL, *obj_ref = NULL, *state_ref = NULL;
+                       char *obj_name = NULL, *op = NULL, *version = NULL;
+                       int obj_id = 0;
+
+                       test_ref = xmlGetProp(c, "test_ref");
+
+                       tst = tests->get(tests, test_ref);
+                       if (tst)
+                       {
+                               for (cur = tst->xmlChildrenNode; cur != NULL; cur = cur->next)
+                               {
+                                       /* ignore empty or blank nodes */
+                                       if (xmlIsBlankNode(cur))
+                                       {
+                                               continue;
+                                       }
+                                       if (!xmlStrcmp(cur->name, "object"))
+                                       {
+                                               obj_ref = xmlGetProp(cur, "object_ref");
+                                               obj_name = objects->get(objects, obj_ref);
+                                               if (obj_name)
+                                               {
+                                                       /* check if object is already in database */
+                                                       e = db->query(db, "SELECT id FROM packages WHERE "
+                                                                               "name = ?", DB_TEXT, obj_name, DB_INT);
+                                                       if (e)
+                                                       {
+                                                               if (!e->enumerate(e, &obj_id))
+                                                               {
+                                                                       obj_id = 0;
+                                                               }
+                                                               e->destroy(e);
+                                                       }
+                                               }
+                                       }
+                                       else if (!xmlStrcmp(cur->name, "state"))
+                                       {
+                                               state_ref = xmlGetProp(cur, "state_ref");
+
+                                               ste = states->get(states, state_ref);
+                                               if (ste)
+                                               {
+                                                       for (s = ste->xmlChildrenNode; s != NULL; s = s->next)
+                                                       {
+                                                               if (!xmlStrcmp(s->name, "evr"))
+                                                               {
+                                                                       op = xmlGetProp(s, "operation");
+                                                                       version = xmlNodeGetContent(s);
+                                                               }
+                                                       }
+                                               }
+                                       }
+                               }
+                       }
+                       oval->add_criterion(oval, test_ref, state_ref, obj_ref, obj_name,
+                                                               obj_id, op, version);
+               }
+               else if (!xmlStrcmp(c->name, "criteria"))
+               {
+                       extract_criteria(oval, c);
+               }
+       }
+}
+
+bool is_vulnerable(array_t *products, int pid, char *release, char *version)
+{
+       char command[BUF_LEN];
+       int i, product_id;
+
+       for (i = 0; i < array_count(products); i++)
+       {
+               if (array_get(products, i, &product_id) && product_id == pid)
+               {
+                       snprintf(command, BUF_LEN, "dpkg --compare-versions %s lt %s",
+                                                                               release, version);
+                       return system(command) == 0;
+               }
+       }
+       return FALSE;
+}
+
+/**
+ * Process an OVAL definition file
+ */
+static int process_oval_file(char *path, char *os, char *archs)
+{
+       xmlDocPtr doc;
+       xmlNodePtr defs = NULL, objs = NULL, tsts = NULL, stes = NULL;
+       xmlNodePtr cur, def, tst, obj, ste, c;
+       oval_t *oval = NULL;
+       enumerator_t *e;
+       array_t *products;
+       char *db_uri, *cve_ref, *description, *title, *start, *stop;
+       uint32_t def_count = 0, tst_count = 0, obj_count = 0, ste_count = 0;
+       uint32_t complete_count = 0;
+       int pid = 0, result = EXIT_FAILURE;
+
+    xmlInitParser();
+
+       /* parsing OVAL XML file */
+       doc = xmlReadFile(path, NULL, 0);
+       if (!doc)
+       {
+               DBG1(DBG_LIB, "  could not be parsed \"%s\"", path);
+               goto end;
+       }
+
+       /* check out the XML document */
+       cur = xmlDocGetRootElement(doc);
+       if (!cur)
+       {
+               DBG1(DBG_LIB, "  empty OVAL document");
+               goto end;
+       }
+       if (xmlStrcmp(cur->name, "oval_definitions"))
+       {
+               DBG1(DBG_LIB, "  no oval_definitions element found");
+               goto end;
+       }
+
+       /* Now walk the tree, handling nodes as we go */
+       for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next)
+       {
+               /* ignore empty or blank nodes */
+               if (xmlIsBlankNode(cur))
+               {
+                       continue;
+               }
+               if (!xmlStrcmp(cur->name, "definitions"))
+               {
+                       defs = cur;
+               }
+               else if (!xmlStrcmp(cur->name, "objects"))
+               {
+                       objs = cur;
+               }
+               else if(!xmlStrcmp(cur->name, "tests"))
+               {
+                       tsts = cur;
+               }
+               else if (!xmlStrcmp(cur->name, "states"))
+               {
+                       stes = cur;
+               }
+       }
+
+       if (!defs || !objs || !tsts || !stes)
+       {
+               if (!defs)
+               {
+                       DBG1(DBG_LIB, "  no definitions element found");
+               }
+               if (!objs)
+               {
+                       DBG1(DBG_LIB, "  no objects element found");
+               }
+               if (!tsts)
+               {
+                       DBG1(DBG_LIB, "  no tests element found");
+               }
+               if (!stes)
+               {
+                       DBG1(DBG_LIB, "  no states element found");
+               }
+               goto end;
+       }
+
+       /* connect package database */
+       db_uri = lib->settings->get_str(lib->settings, "oval-updater.database", NULL);
+       if (!db_uri)
+       {
+               DBG1(DBG_LIB, "database URI sec-updater.database not set");
+               goto end;
+       }
+       db = lib->db->create(lib->db, db_uri);
+       if (!db)
+       {
+               DBG1(DBG_LIB, "could not connect to database '%s'", db_uri);
+               goto end;
+       }
+
+       /* check if architectures of a given os are already in database */
+       products = array_create(sizeof(int), 5);
+       start = archs;
+
+       while (TRUE)
+       {
+               char product[32];
+
+               stop = strchrnul(start, ' ');
+               if (stop == NULL)
+               {
+                       break;
+               }
+               snprintf(product, sizeof(product), "%s %.*s", os, stop - start, start);
+
+               e = db->query(db, "SELECT id FROM products WHERE name = ?",
+                                                 DB_TEXT, product, DB_INT);
+               if (e)
+               {
+                       if (e->enumerate(e, &pid))
+                       {
+                               array_insert(products, -1, &pid);
+                               DBG1(DBG_LIB, "%s (%d)", product, pid);
+                       }
+                       e->destroy(e);
+               }
+               if (*stop == '\0')
+               {
+                       break;
+               }
+               start = stop + 1;
+       }
+
+       /* create tests hash table for fast access */
+       tests = hashtable_create(hashtable_hash_str, hashtable_equals_str, 32768);
+
+       /* enumerate tests */
+       for (tst = tsts->xmlChildrenNode; tst != NULL; tst = tst->next)
+       {
+               /* ignore empty or blank nodes */
+               if (xmlIsBlankNode(tst))
+               {
+                       continue;
+               }
+               if (!xmlStrcmp(tst->name, "dpkginfo_test"))
+               {
+                       tests->put(tests, xmlGetProp(tst, "id"), tst);
+                       tst_count++;
+               }
+       }
+       DBG1(DBG_LIB, "%u tests", tst_count);
+
+       /* create objects hash table for fast access */
+       objects = hashtable_create(hashtable_hash_str, hashtable_equals_str, 4096);
+
+       /* enumerate objects */
+       for (obj = objs->xmlChildrenNode; obj != NULL; obj = obj->next)
+       {
+               /* ignore empty or blank nodes */
+               if (xmlIsBlankNode(obj))
+               {
+                       continue;
+               }
+               if (!xmlStrcmp(obj->name, "dpkginfo_object"))
+               {
+                       for (cur = obj->xmlChildrenNode; cur != NULL; cur = cur->next)
+                       {
+                               /* ignore empty or blank nodes */
+                               if (xmlIsBlankNode(cur))
+                               {
+                                       continue;
+                               }
+                               if (!xmlStrcmp(cur->name, "name"))
+                               {
+                                       objects->put(objects, xmlGetProp(obj, "id"),
+                                                                                 xmlNodeGetContent(cur));
+                                       obj_count++;
+                               }
+                       }
+               }
+       }
+       DBG1(DBG_LIB, "%u objects", obj_count);
+
+       /* create states hash table for fast access */
+       states = hashtable_create(hashtable_hash_str, hashtable_equals_str, 16384);
+
+       /* enumerate states */
+       for (ste = stes->xmlChildrenNode; ste != NULL; ste = ste->next)
+       {
+               /* ignore empty or blank nodes */
+               if (xmlIsBlankNode(ste))
+               {
+                       continue;
+               }
+               if (!xmlStrcmp(ste->name, "dpkginfo_state"))
+               {
+                       states->put(states, xmlGetProp(ste, "id"), ste);
+                       ste_count++;
+               }
+       }
+       DBG1(DBG_LIB, "%u states", ste_count);
+
+       /* enumerate definitions */
+       for (def = defs->xmlChildrenNode; def != NULL; def = def->next)
+       {
+               /* ignore empty or blank nodes */
+               if (xmlIsBlankNode(def))
+               {
+                       continue;
+               }
+               if (!xmlStrcmp(def->name, "definition") &&
+                       !xmlStrcmp(xmlGetProp(def, "class"), "vulnerability"))
+               {
+                       cve_ref = description = title = NULL;
+
+                       for (cur = def->xmlChildrenNode; cur != NULL; cur = cur->next)
+                       {
+                               /* ignore empty or blank nodes */
+                               if (xmlIsBlankNode(cur))
+                               {
+                                       continue;
+                               }
+                               if (!xmlStrcmp(cur->name, "metadata"))
+                               {
+                                       for (c = cur->xmlChildrenNode; c != NULL; c = c->next)
+                                       {
+                                               /* ignore empty or blank nodes */
+                                               if (xmlIsBlankNode(c))
+                                               {
+                                                       continue;
+                                               }
+                                               if (!xmlStrcmp(c->name, "reference"))
+                                               {
+                                                       cve_ref = xmlGetProp(c, "ref_id");
+                                               }
+                                               else if (!xmlStrcmp(c->name, "description"))
+                                               {
+                                                       description = xmlNodeGetContent(c);
+                                               }
+                                               else if (!xmlStrcmp(c->name, "title"))
+                                               {
+                                                       title = xmlNodeGetContent(c);
+                                               }
+                                       }
+                                       if (cve_ref || title)
+                                       {
+                                               if (!cve_ref)
+                                               {
+                                                       cve_ref = title;
+                                               }
+                                               def_count++;
+                                       }
+                                       oval = oval_create(cve_ref, description);
+                               }
+                               else if (!xmlStrcmp(cur->name, "criteria"))
+                               {
+                                       extract_criteria(oval, cur);
+                               }
+                       }
+               }
+               if (oval)
+               {
+                       oval->print(oval);
+
+                       if (oval->is_complete(oval))
+                       {
+                               enumerator_t *enumerator;
+                               char *obj_name, *version, *release;
+                               int obj_id, vid, pid, security;
+
+                               enumerator = oval->create_criterion_enumerator(oval);
+                               while (enumerator->enumerate(enumerator, &obj_name, &obj_id,
+                                                                                                                &version))
+                               {
+                                       e = db->query(db, "SELECT id, product, release, security "
+                                               "FROM versions WHERE package = ?", DB_INT, obj_id,
+                                               DB_INT, DB_INT, DB_TEXT, DB_INT);
+                                       if (e)
+                                       {
+                                               while (e->enumerate(e, &vid, &pid, &release, &security))
+                                               {
+                                                       if (is_vulnerable(products, pid, release, version))
+                                                       {
+                                                               DBG1(DBG_LIB, "xxx%-14s %s %s (%d) %s (%d, %d)",
+                                                                        cve_ref, security ? "*" : " ",
+                                                                        obj_name, obj_id, release, pid, vid);
+                                                       }
+                                               }
+                                               e->destroy(e);
+                                       }
+                               }
+                               enumerator->destroy(enumerator);
+                               complete_count++;
+                       }
+                       oval->destroy(oval);
+                       oval = NULL;
+               }
+       }
+       DBG1(DBG_LIB, "%u of %u definitions are complete", complete_count, def_count);
+
+       db->destroy(db);
+       tests->destroy(tests);
+       objects->destroy(objects);
+       states->destroy(states);
+       xmlFreeDoc(doc);
+       result = EXIT_SUCCESS;
+
+end:
+       xmlCleanupParser();
+       return result;
+}
+
+static int do_args(int argc, char *argv[])
+{
+       char *filename = NULL, *os = NULL, *archs = NULL;
+
+       /* reinit getopt state */
+       optind = 0;
+
+       while (TRUE)
+       {
+               int c;
+
+               struct option long_opts[] = {
+                       { "help", no_argument, NULL, 'h' },
+                       { "debug", required_argument, NULL, 'd' },
+                       { "file", required_argument, NULL, 'f' },
+                       { "os", required_argument, NULL, 'o' },
+                       { "archs", required_argument, NULL, 'a' },
+                       { "quiet", no_argument, NULL, 'q' },
+                       { 0,0,0,0 }
+               };
+
+               c = getopt_long(argc, argv, "hd:f:o:a:q", long_opts, NULL);
+               switch (c)
+               {
+                       case EOF:
+                               break;
+                       case 'h':
+                               usage();
+                               exit(EXIT_SUCCESS);
+                       case 'd':
+                               debug_level = atoi(optarg);
+                               continue;
+                       case 'f':
+                               filename = optarg;
+                               continue;
+                       case 'o':
+                               os = optarg;
+                               continue;
+                       case 'a':
+                               archs = optarg;
+                               continue;
+                       case 'q':
+                               stderr_quiet = TRUE;
+                               continue;
+               }
+               break;
+       }
+
+       if (filename && os && archs)
+       {
+               return process_oval_file(filename, os, archs);
+       }
+       else
+       {
+               usage();
+               exit(EXIT_FAILURE);
+       }
+}
+
+int main(int argc, char *argv[])
+{
+       /* enable attest debugging hook */
+       dbg = oval_updater_dbg;
+       openlog("oval-updater", 0, LOG_DEBUG);
+
+       atexit(cleanup);
+
+       /* initialize library */
+       if (!library_init(NULL, "oval-updater"))
+       {
+               exit(SS_RC_LIBSTRONGSWAN_INTEGRITY);
+       }
+       if (!lib->plugins->load(lib->plugins,
+                       lib->settings->get_str(lib->settings, "oval-updater.load",
+                                                                                                 "sqlite curl")))
+       {
+               exit(SS_RC_INITIALIZATION_FAILED);
+       }
+       exit(do_args(argc, argv));
+}
diff --git a/src/oval-updater/oval-updater.sh b/src/oval-updater/oval-updater.sh
new file mode 100755 (executable)
index 0000000..aa058d0
--- /dev/null
@@ -0,0 +1,53 @@
+#!/bin/sh
+
+DIR="/etc/pts"
+OVAL_DIR="$DIR/oval"
+DATE=`date +%Y%m%d-%H%M`
+UBUNTU="https://people.canonical.com/~ubuntu-security/oval"
+UBUNTU_VERSIONS="bionic xenial"
+DEBIAN="https://www.debian.org/security/oval"
+DEBIAN_VERSIONS="stretch jessie wheezy"
+CMD=/usr/sbin/oval-updater
+CMD_LOG="$DIR/logs/$DATE-oval-update.log"
+DEL_LOG=1
+
+mkdir -p $OVAL_DIR
+cd $OVAL_DIR
+
+# Download Ubuntu OVAL files
+
+for v in $UBUNTU_VERSIONS
+do
+  wget -nv $UBUNTU/com.ubuntu.$v.cve.oval.xml -O $v-oval.xml
+done
+
+# Download Debian distribution information
+
+for v in $DEBIAN_VERSIONS
+do
+  wget -nv $DEBIAN/oval-definitions-$v.xml -O $v-oval.xml
+done
+
+# Run oval-updater
+
+$CMD --os "Ubuntu 18.04" --archs "x86_64" --file /etc/pts/oval/bionic-oval.xml --debug 2 \
+     2> /etc/pts/oval/bionic-oval.log
+
+$CMD --os "Ubuntu 16.04" --archs "x86_64" --file /etc/pts/oval/xenial-oval.xml --debug 2 \
+     2> /etc/pts/oval/xenial-oval.log
+
+$CMD --os "Debian 9.0" --archs "x86_64 armhf" --file /etc/pts/oval/stretch-oval.xml --debug 2 \
+     2> /etc/pts/oval/stretch-oval.log
+
+$CMD --os "Debian 8.0" --archs "x86_64 armhf" --file /etc/pts/oval/jessie-oval.xml --debug 2 \
+     2> /etc/pts/oval/jessie-oval.log
+
+$CMD --os "Debian 7.0" --archs "x86_64 armhf" --file /etc/pts/oval/wheezy-oval.xml --debug 2 \
+     2> /etc/pts/oval/wheezy-oval.log
+
+
+if [ $DEL_LOG -eq 1 ]
+then
+  rm $CMD_LOG
+  echo "no new vulnerabilities found"
+fi
diff --git a/src/oval-updater/oval.c b/src/oval-updater/oval.c
new file mode 100644 (file)
index 0000000..6f826f2
--- /dev/null
@@ -0,0 +1,255 @@
+/*
+ * Copyright (C) 2018 Andreas Steffen
+ * HSR Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+#include "oval.h"
+
+typedef struct private_oval_t private_oval_t;
+typedef struct criterion_t criterion_t;
+
+/**
+ * Private data of an oval_t object.
+ *
+ */
+struct private_oval_t {
+       /**
+        * Public oval_t interface.
+        */
+       oval_t public;
+
+       /**
+        * CVE ID
+        */
+       char *cve;
+
+       /**
+        * Description
+        */
+       char *description;
+
+       /**
+        * List of criteria
+        */
+       linked_list_t *criteria;
+
+       /**
+         * Complete criterion consiting of object name and state */
+       bool complete;
+};
+
+/**
+ * This object defines an OVAL logical statement
+ */
+struct criterion_t {
+       /** Test reference */
+       char *tst_ref;
+       /** State reference */
+       char *ste_ref;
+       /** Object reference */
+       char *obj_ref;
+       /** Object name */
+       char *obj_name;
+       /** Object primary key into packages table */
+       int obj_id;
+       /** Package version comparison operation */
+       char *op;
+       /** Package version */
+       char *version;
+       /** Complete criterion */
+       bool complete;
+};
+
+METHOD(oval_t, add_criterion, void,
+       private_oval_t *this, char *tst, char *ste, char *obj, char *obj_name,
+       int obj_id, char *op, char *version)
+{
+       criterion_t *criterion;
+
+       INIT(criterion,
+               .tst_ref = tst,
+               .ste_ref = ste,
+               .obj_ref = obj,
+               .obj_name = obj_name,
+               .obj_id = obj_id,
+               .op = op,
+               .version = version,
+               .complete = tst && ste && obj && obj_name && obj_id && op && version &&
+                                       !streq(version, "0:0"),
+       )
+
+       if (criterion->complete)
+       {
+               this->complete = TRUE;
+       }
+       this->criteria->insert_last(this->criteria, criterion);
+}
+
+CALLBACK(criterion_filter, bool,
+       void *null, enumerator_t *orig, va_list args)
+{
+       criterion_t *criterion;
+       char **obj_name, **version;
+       int *obj_id;
+
+       VA_ARGS_VGET(args, obj_name, obj_id, version);
+
+       while (orig->enumerate(orig, &criterion))
+       {
+               if (criterion->complete && streq(criterion->op, "less than"))
+               {
+                       *obj_name = criterion->obj_name;
+                       *obj_id = criterion->obj_id;
+                       *version = criterion->version;
+                       return TRUE;
+               }
+       }
+       return FALSE;
+}
+
+METHOD(oval_t, create_criterion_enumerator, enumerator_t*,
+       private_oval_t *this)
+{
+       return enumerator_create_filter(
+                       this->criteria->create_enumerator(this->criteria), criterion_filter,
+                       NULL, NULL);
+}
+
+METHOD(oval_t, is_complete, bool,
+       private_oval_t *this)
+{
+       return this->complete;
+}
+
+void print_metadata(private_oval_t *this, int level)
+{
+       if (this->cve)
+       {
+               if (level == 2)
+               {
+                       DBG2(DBG_LIB, "%s", this->cve);
+               }
+               else
+               {
+                       DBG3(DBG_LIB, "%s", this->cve);
+               }
+       }
+
+       if (this->description)
+       {
+               const int max_char = 150;
+               char line[max_char + 1];
+
+               /* truncate description to max_char characters */
+               if (strlen(this->description) > max_char)
+               {
+                       strncpy(line, this->description, max_char);
+                       line[max_char] = '\0';
+                       if (level == 2)
+                       {
+                               DBG2(DBG_LIB, "  %s...", line);
+                       }
+                       else
+                       {
+                               DBG3(DBG_LIB, "  %s...", line);
+                       }
+               }
+               else
+               {
+                       if (level == 2)
+                       {
+                               DBG2(DBG_LIB, "  %s", this->description);
+                       }
+                       else
+                       {
+                               DBG3(DBG_LIB, "  %s", this->description);
+                       }
+               }
+       }
+}
+
+METHOD(oval_t, print, void,
+       private_oval_t *this)
+{
+       enumerator_t *enumerator;
+       criterion_t *criterion;
+
+       print_metadata(this, this->complete ? 2 : 3);
+
+       enumerator = this->criteria->create_enumerator(this->criteria);
+       while (enumerator->enumerate(enumerator, &criterion))
+       {
+               if (criterion->complete)
+               {
+                       DBG2(DBG_LIB, "  %s", criterion->tst_ref);
+                       DBG2(DBG_LIB, "    %s", criterion->obj_ref);
+                       DBG2(DBG_LIB, "      %s (%d)", criterion->obj_name,
+                                                                                  criterion->obj_id);
+                       DBG2(DBG_LIB, "    %s", criterion->ste_ref);
+                       DBG2(DBG_LIB, "      %s '%s'", criterion->op, criterion->version);
+               }
+               else
+               {
+                       DBG3(DBG_LIB, "  %s", criterion->tst_ref);
+                       if (criterion->obj_ref)
+                       {
+                               DBG3(DBG_LIB, "    %s", criterion->obj_ref);
+                               if (criterion->obj_name)
+                               {
+                                       DBG3(DBG_LIB, "      %s (%d)", criterion->obj_name,
+                                                                                                  criterion->obj_id);
+                               }
+                       }
+                       if (criterion->ste_ref)
+                       {
+                               DBG3(DBG_LIB, "    %s", criterion->ste_ref);
+                               if (criterion->version)
+                               {
+                                       DBG3(DBG_LIB, "      %s '%s'", criterion->op,
+                                                                                                  criterion->version);
+                               }
+                       }
+               }
+       }
+       enumerator->destroy(enumerator);
+}
+
+METHOD(oval_t, destroy, void,
+       private_oval_t *this)
+{
+       this->criteria->destroy_function(this->criteria, (void*)free);
+       free(this);
+}
+
+/**
+ * See header
+ */
+oval_t* oval_create(char *cve, char *description)
+{
+       private_oval_t *this;
+
+       INIT(this,
+               .public = {
+                       .add_criterion = _add_criterion,
+                       .create_criterion_enumerator = _create_criterion_enumerator,
+                       .is_complete = _is_complete,
+                       .print = _print,
+                       .destroy = _destroy,
+               },
+               .cve = cve,
+               .description = description,
+               .criteria = linked_list_create(),
+       )
+
+       return &this->public;
+}
diff --git a/src/oval-updater/oval.h b/src/oval-updater/oval.h
new file mode 100644 (file)
index 0000000..f04eb73
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2018 Andreas Steffen
+ * HSR Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+/**
+ * @defgroup oval oval
+ * @{ @ingroup tnccs_11
+ */
+
+#ifndef OVAL_H_
+#define OVAL_H_
+
+typedef struct oval_t oval_t;
+
+#include <library.h>
+
+/**
+ * OVAL vulnerability object
+ */
+struct oval_t {
+
+       /**
+        * Add an OVAL logical criterion
+        *
+        @param tst                             reference to OVAL test
+        @param ste             reference to OVAL status
+        @param obj                             reference to OVAL object
+        @param obj_name                OVAL object name
+        @param obj_id                  primary key of object in packages table
+        @param op                              package version comparison operation
+        @param version                 package version
+        */
+       void (*add_criterion)(oval_t *this, char *tst, char* ste, char *obj,
+                                                 char *obj_name, int obj_id, char *op, char *version);
+
+       /**
+        * Enumerate over the complete criteria
+        *
+        @return                                criterion enumerator
+        */
+       enumerator_t* (*create_criterion_enumerator)(oval_t *this);
+
+       /**
+        * Does OVAL vulnerability object contain at least one complete criterion?
+        *
+        @return                                TRUE if there is at least one complete criterion
+        */
+       bool (*is_complete)(oval_t *this);
+
+       /**
+        * Print an oval_t object.
+        */
+       void (*print)(oval_t *this);
+
+       /**
+        * Destroys a oval_t object.
+        */
+       void (*destroy)(oval_t *this);
+};
+
+/**
+ * Create an OVAL vulnerability object
+ *
+ * @param cve                          CVE number
+ * @param description          vulnerability description
+ */
+oval_t* oval_create(char *cve, char* description);
+
+#endif /** OVAL_H_ @}*/