From 24008b339b4088ce268e58fcf173bd3efc870b54 Mon Sep 17 00:00:00 2001 From: Andreas Steffen Date: Mon, 8 Oct 2018 10:48:00 +0200 Subject: [PATCH] oval-updater: Find vulnerabilities in Linux packages --- configure.ac | 7 +- src/Makefile.am | 4 + src/oval-updater/.gitignore | 1 + src/oval-updater/Makefile.am | 26 ++ src/oval-updater/oval-updater.8 | 0 src/oval-updater/oval-updater.8.in | 0 src/oval-updater/oval-updater.c | 596 +++++++++++++++++++++++++++++ src/oval-updater/oval-updater.sh | 53 +++ src/oval-updater/oval.c | 255 ++++++++++++ src/oval-updater/oval.h | 80 ++++ 10 files changed, 1020 insertions(+), 2 deletions(-) create mode 100644 src/oval-updater/.gitignore create mode 100644 src/oval-updater/Makefile.am create mode 100644 src/oval-updater/oval-updater.8 create mode 100644 src/oval-updater/oval-updater.8.in create mode 100644 src/oval-updater/oval-updater.c create mode 100755 src/oval-updater/oval-updater.sh create mode 100644 src/oval-updater/oval.c create mode 100644 src/oval-updater/oval.h diff --git a/configure.ac b/configure.ac index 0ff2819c67..00e06c8f3b 100644 --- a/configure.ac +++ b/configure.ac @@ -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 diff --git a/src/Makefile.am b/src/Makefile.am index 6eacbe2930..c583dee7ef 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -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 index 0000000000..287b82b575 --- /dev/null +++ b/src/oval-updater/.gitignore @@ -0,0 +1 @@ +oval-updater diff --git a/src/oval-updater/Makefile.am b/src/oval-updater/Makefile.am new file mode 100644 index 0000000000..522720b6fc --- /dev/null +++ b/src/oval-updater/Makefile.am @@ -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 index 0000000000..e69de29bb2 diff --git a/src/oval-updater/oval-updater.8.in b/src/oval-updater/oval-updater.8.in new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/oval-updater/oval-updater.c b/src/oval-updater/oval-updater.c new file mode 100644 index 0000000000..054f22e4f9 --- /dev/null +++ b/src/oval-updater/oval-updater.c @@ -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 . + * + * 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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#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 ] [--quiet] --os --archs \n\ + --file \n\n\ + Options:\n\ + --help print usage information\n\ + --debug set debug level\n\ + --quiet suppress debug output to stderr\n\ + --os operating system\n\ + --archs space separated enumeration of architectures\n\ + --file 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 index 0000000000..aa058d0f39 --- /dev/null +++ b/src/oval-updater/oval-updater.sh @@ -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 index 0000000000..6f826f24e5 --- /dev/null +++ b/src/oval-updater/oval.c @@ -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 . + * + * 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 index 0000000000..f04eb733f4 --- /dev/null +++ b/src/oval-updater/oval.h @@ -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 . + * + * 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 + +/** + * 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_ @}*/ -- 2.47.2