diff options
author | Yves-Alexis Perez <corsac@corsac.net> | 2017-09-01 17:21:25 +0200 |
---|---|---|
committer | Yves-Alexis Perez <corsac@corsac.net> | 2017-09-01 17:21:25 +0200 |
commit | 11d6b62db969bdd808d0f56706cb18f113927a31 (patch) | |
tree | 8aa7d8fb611c3da6a3523cb78a082f62ffd0dac8 /src/sw-collector/sw-collector.c | |
parent | bba25e2ff6c4a193acb54560ea4417537bd2954e (diff) | |
download | vyos-strongswan-11d6b62db969bdd808d0f56706cb18f113927a31.tar.gz vyos-strongswan-11d6b62db969bdd808d0f56706cb18f113927a31.zip |
New upstream version 5.6.0
Diffstat (limited to 'src/sw-collector/sw-collector.c')
-rw-r--r-- | src/sw-collector/sw-collector.c | 652 |
1 files changed, 652 insertions, 0 deletions
diff --git a/src/sw-collector/sw-collector.c b/src/sw-collector/sw-collector.c new file mode 100644 index 000000000..e673dd657 --- /dev/null +++ b/src/sw-collector/sw-collector.c @@ -0,0 +1,652 @@ +/* + * Copyright (C) 2017 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 <stdio.h> +#include <string.h> +#include <errno.h> +#include <getopt.h> +#include <unistd.h> +#ifdef HAVE_SYSLOG +# include <syslog.h> +#endif + +#include "sw_collector_db.h" +#include "sw_collector_history.h" +#include "sw_collector_rest_api.h" +#include "sw_collector_dpkg.h" +# +#include <library.h> +#include <utils/debug.h> +#include <utils/lexparser.h> + +#include <swid_gen/swid_gen.h> + +/** + * global debug output variables + */ +static int debug_level = 2; +static bool stderr_quiet = FALSE; +static int count = 0; + +typedef enum collector_op_t collector_op_t; + +enum collector_op_t { + COLLECTOR_OP_EXTRACT, + COLLECTOR_OP_LIST, + COLLECTOR_OP_UNREGISTERED, + COLLECTOR_OP_GENERATE, + COLLECTOR_OP_MIGRATE +}; + +/** + * sw_collector dbg function + */ +static void sw_collector_dbg(debug_t group, level_t level, char *fmt, ...) +{ + va_list args; + + if (level <= debug_level) + { + if (!stderr_quiet) + { + va_start(args, fmt); + vfprintf(stderr, fmt, args); + fprintf(stderr, "\n"); + va_end(args); + } + +#ifdef HAVE_SYSLOG + { + int priority = LOG_INFO; + char buffer[8192]; + char *current = buffer, *next; + + /* 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; + } + } +#endif /* HAVE_SYSLOG */ + } +} + +/** + * atexit handler + */ +static void cleanup(void) +{ + library_deinit(); +#ifdef HAVE_SYSLOG + closelog(); +#endif +} + +/** + * Display usage of sw-collector command + */ +static void usage(void) +{ + printf("\ +Usage:\n\ + sw-collector --help\n\ + sw-collector [--debug <level>] [--quiet] [--count <event count>]\n\ + sw-collector [--debug <level>] [--quiet] [--installed|--removed] \ +--list|-unregistered\n\ + sw-collector [--debug <level>] [--quiet] [--installed|--removed] \ +[--full] --generate\n\ + sw-collector [--debug <level>] [--quiet] --migrate\n"); +} + +/** + * Parse command line options + */ +static collector_op_t do_args(int argc, char *argv[], bool *full_tags, + sw_collector_db_query_t *query_type) +{ + collector_op_t op = COLLECTOR_OP_EXTRACT; + bool installed = FALSE, removed = FALSE, full = FALSE; + + /* reinit getopt state */ + optind = 0; + + while (TRUE) + { + int c; + + struct option long_opts[] = { + { "help", no_argument, NULL, 'h' }, + { "count", required_argument, NULL, 'c' }, + { "debug", required_argument, NULL, 'd' }, + { "full", no_argument, NULL, 'f' }, + { "generate", no_argument, NULL, 'g' }, + { "installed", no_argument, NULL, 'i' }, + { "list", no_argument, NULL, 'l' }, + { "migrate", no_argument, NULL, 'm' }, + { "quiet", no_argument, NULL, 'q' }, + { "removed", no_argument, NULL, 'r' }, + { "unregistered", no_argument, NULL, 'u' }, + { 0,0,0,0 } + }; + + c = getopt_long(argc, argv, "hc:d:fgilmqru", long_opts, NULL); + switch (c) + { + case EOF: + break; + case 'h': + usage(); + exit(SUCCESS); + break; + case 'c': + count = atoi(optarg); + continue; + case 'd': + debug_level = atoi(optarg); + continue; + case 'f': + full = TRUE; + continue; + case 'g': + op = COLLECTOR_OP_GENERATE; + continue; + case 'i': + installed = TRUE; + continue; + case 'l': + op = COLLECTOR_OP_LIST; + continue; + case 'm': + op = COLLECTOR_OP_MIGRATE; + continue; + case 'q': + stderr_quiet = TRUE; + continue; + case 'r': + removed = TRUE; + continue; + case 'u': + op = COLLECTOR_OP_UNREGISTERED; + continue; + default: + usage(); + exit(EXIT_FAILURE); + } + break; + } + + if ((!installed && !removed) || (installed && removed)) + { + *query_type = SW_QUERY_ALL; + } + else if (installed) + { + *query_type = SW_QUERY_INSTALLED; + } + else + { + *query_type = SW_QUERY_REMOVED; + } + *full_tags = full; + + return op; +} + +/** + * Extract software events from apt history log files + */ +static int extract_history(sw_collector_db_t *db) +{ + sw_collector_history_t *history = NULL; + uint32_t epoch, last_eid, eid = 0; + char *history_path, *last_time = NULL, rfc_time[21]; + chunk_t *h, history_chunk, line, cmd; + int status = EXIT_FAILURE; + bool skip = TRUE; + + /* open history file for reading */ + history_path = lib->settings->get_str(lib->settings, "%s.history", NULL, + lib->ns); + if (!history_path) + { + fprintf(stderr, "sw-collector.history path not set.\n"); + return EXIT_FAILURE; + } + h = chunk_map(history_path, FALSE); + if (!h) + { + fprintf(stderr, "opening '%s' failed: %s", history_path, + strerror(errno)); + return EXIT_FAILURE; + } + history_chunk = *h; + + /* Instantiate history extractor */ + history = sw_collector_history_create(db, 1); + if (!history) + { + return EXIT_FAILURE; + } + + /* retrieve last event in database */ + if (!db->get_last_event(db, &last_eid, &epoch, &last_time) || !last_eid) + { + goto end; + } + DBG0(DBG_IMC, "Last-Event: %s, eid = %u, epoch = %u", + last_time, last_eid, epoch); + + /* parse history file */ + while (fetchline(&history_chunk, &line)) + { + if (line.len == 0) + { + continue; + } + if (!extract_token(&cmd, ':', &line)) + { + fprintf(stderr, "terminator symbol ':' not found.\n"); + goto end; + } + if (match("Start-Date", &cmd)) + { + if (!history->extract_timestamp(history, line, rfc_time)) + { + goto end; + } + + /* have we reached new history entries? */ + if (skip && strcmp(rfc_time, last_time) > 0) + { + skip = FALSE; + } + if (skip) + { + continue; + } + + /* insert new event into database */ + eid = db->add_event(db, rfc_time); + if (!eid) + { + goto end; + } + DBG1(DBG_IMC, "Start-Date: %s, eid = %u, epoch = %u", + rfc_time, eid, epoch); + } + else if (skip) + { + /* skip old history entries which have already been processed */ + continue; + } + else if (match("Install", &cmd)) + { + DBG1(DBG_IMC, " Install:"); + if (!history->extract_packages(history, line, eid, SW_OP_INSTALL)) + { + goto end; + } + } + else if (match("Upgrade", &cmd)) + { + DBG1(DBG_IMC, " Upgrade:"); + if (!history->extract_packages(history, line, eid, SW_OP_UPGRADE)) + { + goto end; + } + } + else if (match("Remove", &cmd)) + { + DBG1(DBG_IMC, " Remove:"); + if (!history->extract_packages(history, line, eid, SW_OP_REMOVE)) + { + goto end; + } + } + else if (match("Purge", &cmd)) + { + DBG1(DBG_IMC, " Purge:"); + if (!history->extract_packages(history, line, eid, SW_OP_REMOVE)) + { + goto end; + } + } + else if (match("End-Date", &cmd)) + { + /* Process 'count' events at a time */ + if (count > 0 && eid - last_eid == count) + { + fprintf(stderr, "added %d events\n", count); + goto end; + } + } + } + + if (history->merge_installed_packages(history)) + { + status = EXIT_SUCCESS; + } + +end: + free(last_time); + history->destroy(history); + chunk_unmap(h); + + return status; +} + +/** + * List all endpoint software identifiers stored in local collector database + */ +static int list_identifiers(sw_collector_db_t *db, sw_collector_db_query_t type) +{ + enumerator_t *e; + char *name, *package, *version; + uint32_t sw_id, count = 0, installed_count = 0, removed_count, installed; + + e = db->create_sw_enumerator(db, type, NULL); + if (!e) + { + return EXIT_FAILURE; + } + while (e->enumerate(e, &sw_id, &name, &package, &version, &installed)) + { + printf("%s,%s,%s,%d\n", name, package, version, installed); + if (installed) + { + installed_count++; + } + count++; + } + removed_count = count - installed_count; + e->destroy(e); + + switch (type) + { + case SW_QUERY_ALL: + DBG1(DBG_IMC, "retrieved %u software identities with %u installed " + "and %u removed", count, installed_count, removed_count); + break; + case SW_QUERY_INSTALLED: + DBG1(DBG_IMC, "retrieved %u installed software identities", count); + break; + case SW_QUERY_REMOVED: + DBG1(DBG_IMC, "retrieved %u removed software identities", count); + break; + } + + return EXIT_SUCCESS; +} + +static bool query_registry(sw_collector_rest_api_t *rest_api, bool installed) +{ + sw_collector_db_query_t type; + enumerator_t *enumerator; + char *sw_id; + int count = 0; + + type = installed ? SW_QUERY_INSTALLED : SW_QUERY_REMOVED; + enumerator = rest_api->create_sw_enumerator(rest_api, type); + if (!enumerator) + { + return FALSE; + } + while (enumerator->enumerate(enumerator, &sw_id)) + { + printf("%s,%s\n", sw_id, installed ? "1" : "0"); + count++; + } + enumerator->destroy(enumerator); + DBG1(DBG_IMC, "%d %s software identifiers not registered", count, + installed ? "installed" : "removed"); + return TRUE; +} + + +/** + * List all endpoint software identifiers stored in local collector database + * that are not registered yet in central collelector database + */ +static int unregistered_identifiers(sw_collector_db_t *db, + sw_collector_db_query_t type) +{ + sw_collector_rest_api_t *rest_api; + int status = EXIT_SUCCESS; + + rest_api = sw_collector_rest_api_create(db); + if (!rest_api) + { + return EXIT_FAILURE; + } + + /* List installed software identifiers not registered centrally */ + if (type != SW_QUERY_REMOVED && !query_registry(rest_api, TRUE)) + { + status = EXIT_FAILURE; + } + + /* List removed software identifiers not registered centrally */ + if (type != SW_QUERY_INSTALLED && !query_registry(rest_api, FALSE)) + { + status = EXIT_FAILURE; + } + rest_api->destroy(rest_api); + + return status; +} + +/** + * Generate ISO 19770-2:2015 SWID tags for [installed|removed|all] + * SW identifiers that are not registered centrally + */ +static int generate_tags(sw_collector_db_t *db, bool full_tags, + sw_collector_db_query_t type) +{ + swid_gen_t * swid_gen; + sw_collector_rest_api_t *rest_api; + char *name, *package, *version, *tag; + enumerator_t *enumerator; + uint32_t sw_id; + bool installed; + int count = 0, installed_count = 0, status = EXIT_FAILURE; + + swid_gen = swid_gen_create(); + rest_api = sw_collector_rest_api_create(db); + if (!rest_api) + { + goto end; + } + + enumerator = rest_api->create_sw_enumerator(rest_api, type); + if (!enumerator) + { + goto end; + } + while (enumerator->enumerate(enumerator, &name)) + { + sw_id = db->get_sw_id(db, name, &package, &version, NULL, &installed); + if (sw_id) + { + tag = swid_gen->generate_tag(swid_gen, name, package, version, + full_tags && installed, FALSE); + if (tag) + { + DBG2(DBG_IMC, " creating %s", name); + printf("%s\n", tag); + free(tag); + count++; + if (installed) + { + installed_count++; + } + } + free(package); + free(version); + } + } + enumerator->destroy(enumerator); + status = EXIT_SUCCESS; + + switch (type) + { + case SW_QUERY_ALL: + DBG1(DBG_IMC, "created %d tags for unregistered software " + "identifiers with %d installed and %d removed", count, + installed_count, count - installed_count); + break; + case SW_QUERY_INSTALLED: + DBG1(DBG_IMC, "created %d tags for unregistered installed software " + "identifiers", count); + break; + case SW_QUERY_REMOVED: + DBG1(DBG_IMC, "created %d tags for unregistered removed software " + "identifiers", count); + break; + } + +end: + swid_gen->destroy(swid_gen); + DESTROY_IF(rest_api); + + return status; +} + +/** + * Append missing architecture suffix to package entries in the database + */ +static int migrate(sw_collector_db_t *db) +{ + sw_collector_dpkg_t *dpkg; + + char *package, *arch, *version; + char package_filter[BUF_LEN]; + int res, count = 0; + int status = EXIT_SUCCESS; + enumerator_t *enumerator; + + dpkg = sw_collector_dpkg_create(); + if (!dpkg) + { + return FAILED; + } + + enumerator = dpkg->create_sw_enumerator(dpkg); + while (enumerator->enumerate(enumerator, &package, &arch, &version)) + { + + /* Look for package names with architecture suffix */ + snprintf(package_filter, BUF_LEN, "%s:%%", package); + + res = db->update_package(db, package_filter, package); + if (res < 0) + { + status = EXIT_FAILURE; + break; + } + else if (res > 0) + { + count += res; + DBG2(DBG_IMC, "%s: removed arch suffix %d times", package, res); + } + } + enumerator->destroy(enumerator); + dpkg->destroy(dpkg); + + DBG1(DBG_IMC, "migrated %d sw identifier records", count); + + return status; +} + + +int main(int argc, char *argv[]) +{ + sw_collector_db_t *db = NULL; + sw_collector_db_query_t query_type; + collector_op_t op; + bool full_tags; + char *uri; + int status = EXIT_FAILURE; + + op = do_args(argc, argv, &full_tags, &query_type); + + /* enable sw_collector debugging hook */ + dbg = sw_collector_dbg; +#ifdef HAVE_SYSLOG + openlog("sw-collector", 0, LOG_DEBUG); +#endif + + atexit(cleanup); + + /* initialize library */ + if (!library_init(NULL, "sw-collector")) + { + exit(SS_RC_LIBSTRONGSWAN_INTEGRITY); + } + + /* load sw-collector plugins */ + if (!lib->plugins->load(lib->plugins, + lib->settings->get_str(lib->settings, "%s.load", PLUGINS, lib->ns))) + { + exit(SS_RC_INITIALIZATION_FAILED); + } + + /* connect to sw-collector database */ + uri = lib->settings->get_str(lib->settings, "%s.database", NULL, lib->ns); + if (!uri) + { + fprintf(stderr, "sw-collector.database URI not set.\n"); + exit(EXIT_FAILURE); + } + db = sw_collector_db_create(uri); + if (!db) + { + fprintf(stderr, "connection to sw-collector database failed.\n"); + exit(EXIT_FAILURE); + } + + switch (op) + { + case COLLECTOR_OP_EXTRACT: + status = extract_history(db); + break; + case COLLECTOR_OP_LIST: + status = list_identifiers(db, query_type); + break; + case COLLECTOR_OP_UNREGISTERED: + status = unregistered_identifiers(db, query_type); + break; + case COLLECTOR_OP_GENERATE: + status = generate_tags(db, full_tags, query_type); + break; + case COLLECTOR_OP_MIGRATE: + status = migrate(db); + break; + } + db->destroy(db); + + exit(status); +} |