diff options
Diffstat (limited to 'src/libimcv/plugins/imv_os/pacman.c')
-rw-r--r-- | src/libimcv/plugins/imv_os/pacman.c | 498 |
1 files changed, 498 insertions, 0 deletions
diff --git a/src/libimcv/plugins/imv_os/pacman.c b/src/libimcv/plugins/imv_os/pacman.c new file mode 100644 index 000000000..f5f52885d --- /dev/null +++ b/src/libimcv/plugins/imv_os/pacman.c @@ -0,0 +1,498 @@ +/* + * Copyright (C) 2012 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 <time.h> + +#include "imv_os_state.h" + +#include <library.h> +#include <utils/debug.h> + +/** + * global debug output variables + */ +static int debug_level = 1; +static bool stderr_quiet = TRUE; + +/** + * pacman dbg function + */ +static void pacman_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" + "ipsec pacman --product <name> --file <filename> [--update]\n"); +} + +/** + * Extract the time the package file was generated + */ +static time_t extract_time(char *line) +{ + struct tm t; + char wday[4], mon[4]; + char* months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; + int i; + + if (sscanf(line, "Generated: %3s %3s %2d %2d:%2d:%2d %4d UTC", wday, mon, + &t.tm_mday, &t.tm_hour, &t.tm_min, &t.tm_sec, &t.tm_year) != 7) + { + return UNDEFINED_TIME; + } + t.tm_isdst = 0; + t.tm_year -= 1900; + t.tm_mon = 12; + + for (i = 0; i < countof(months); i++) + { + if (streq(mon, months[i])) + { + t.tm_mon = i; + break; + } + } + if (t.tm_mon == 12) + { + return UNDEFINED_TIME; + } + + return mktime(&t) - timezone; +} + +/** + * Process a package file and store updates in the database + */ +static void process_packages(char *filename, char *product, bool update) +{ + char *uri, line[12288], *pos; + int count = 0, errored = 0, vulnerable = 0, new_packages = 0; + int new_versions = 0, updated_versions = 0, deleted_versions = 0; + u_int32_t pid = 0; + enumerator_t *e; + database_t *db; + FILE *file; + + /* opening package file */ + printf("loading\"%s\"\n", filename); + file = fopen(filename, "r"); + if (!file) + { + fprintf(stderr, "could not open \"%s\"\n", filename); + exit(EXIT_FAILURE); + } + + /* connect package database */ + uri = lib->settings->get_str(lib->settings, "pacman.database", NULL); + if (!uri) + { + fprintf(stderr, "database URI pacman.database not set\n"); + fclose(file); + exit(EXIT_FAILURE); + } + db = lib->db->create(lib->db, uri); + if (!db) + { + fprintf(stderr, "could not connect to database '%s'\n", uri); + fclose(file); + exit(EXIT_FAILURE); + } + + /* check if product is already in database */ + e = db->query(db, "SELECT id FROM products WHERE name = ?", + DB_TEXT, product, DB_INT); + if (e) + { + if (!e->enumerate(e, &pid)) + { + pid = 0; + } + e->destroy(e); + } + if (!pid) + { + if (db->execute(db, &pid, "INSERT INTO products (name) VALUES (?)", + DB_TEXT, product) != 1) + { + fprintf(stderr, "could not store product '%s' to database\n", + product); + fclose(file); + db->destroy(db); + exit(EXIT_FAILURE); + } + } + + while (fgets(line, sizeof(line), file)) + { + char *package, *version; + char *cur_version, *version_update = NULL, *version_delete = NULL; + bool security, add_version = TRUE; + int cur_security, security_update = 0, security_delete = 0; + u_int32_t gid = 0, vid = 0, vid_update = 0, vid_delete = 0; + time_t gen_time, cur_time; + + count++; + if (count == 1) + { + printf("%s", line); + } + if (count == 3) + { + gen_time = extract_time(line); + + if (gen_time == UNDEFINED_TIME) + { + fprintf(stderr, "could not extract generation time\n"); + fclose(file); + db->destroy(db); + exit(EXIT_FAILURE); + } + printf("Generated: %T\n", &gen_time, TRUE); + } + if (count < 7) + { + continue; + } + + /* look for the package name */ + pos = strchr(line, ' '); + if (!pos) + { + fprintf(stderr, "could not extract package name from '%.*s'\n", + (int)(strlen(line)-1), line); + errored++; + continue; + } + *pos++ = '\0'; + package = line; + + /* look for version string in parentheses */ + if (*pos == '(') + { + version = ++pos; + pos = strchr(pos, ')'); + if (pos) + { + *pos++ = '\0'; + } + else + { + fprintf(stderr, "could not extract package version from " + "'%.*s'\n", (int)(strlen(line)-1), line); + errored++; + continue; + } + } + else + { + /* no version information, skip entry */ + continue; + } + security = (strstr(pos, "[security]") != NULL); + if (security) + { + vulnerable++; + } + + /* handle non-security packages in update mode only */ + if (!update && !security) + { + continue; + } + + /* check if package is already in database */ + e = db->query(db, "SELECT id FROM packages WHERE name = ?", + DB_TEXT, package, DB_INT); + if (e) + { + if (!e->enumerate(e, &gid)) + { + gid = 0; + } + e->destroy(e); + } + if (!gid && security) + { + if (db->execute(db, &gid, "INSERT INTO packages (name) VALUES (?)", + DB_TEXT, package) != 1) + { + fprintf(stderr, "could not store package '%s' to database\n", + package); + fclose(file); + db->destroy(db); + exit(EXIT_FAILURE); + } + new_packages++; + } + + /* check for package versions already in database */ + e = db->query(db, + "SELECT id, release, security, time FROM versions " + "WHERE package = ? AND product = ?", + DB_INT, gid, DB_INT, pid, DB_INT, DB_TEXT, DB_INT, DB_INT); + if (!e) + { + break; + } + while (e->enumerate(e, &vid, &cur_version, &cur_security, &cur_time)) + { + if (streq(version, cur_version)) + { + /* already in data base */ + add_version = FALSE; + break; + } + else if (gen_time > cur_time) + { + if (security) + { + if (cur_security) + { + vid_update = vid; + version_update = strdup(cur_version); + security_update = cur_security; + } + else + { + vid_delete = vid; + version_delete = strdup(cur_version); + security_delete = cur_security; + } + } + else + { + if (!cur_security) + { + vid_update = vid; + version_update = strdup(cur_version); + security_update = cur_security; + } + } + } + else + { + if (security == cur_security) + { + add_version = FALSE; + } + } + } + e->destroy(e); + + if ((!vid && !security) || (vid && !add_version)) + { + free(version_update); + free(version_delete); + continue; + } + + if ((!vid && security) || (vid && !vid_update)) + { + printf("%s (%s) %s\n", package, version, security ? "[s]" : ""); + + if (db->execute(db, &vid, + "INSERT INTO versions " + "(package, product, release, security, time) " + "VALUES (?, ?, ?, ?, ?)", DB_INT, gid, DB_INT, pid, + DB_TEXT, version, DB_INT, security, DB_INT, gen_time) != 1) + { + fprintf(stderr, "could not store version '%s' to database\n", + version); + free(version_update); + free(version_delete); + fclose(file); + db->destroy(db); + exit(EXIT_FAILURE); + } + new_versions++; + } + else + { + printf("%s (%s) %s updated by\n", + package, version_update, security_update ? "[s]" : ""); + printf("%s (%s) %s\n", package, version, security ? "[s]" : ""); + + if (db->execute(db, NULL, + "UPDATE versions SET release = ?, time = ? WHERE id = ?", + DB_TEXT, version, DB_INT, gen_time, DB_INT, vid_update) <= 0) + { + fprintf(stderr, "could not update version '%s' to database\n", + version); + free(version_update); + free(version_delete); + fclose(file); + db->destroy(db); + exit(EXIT_FAILURE); + } + updated_versions++; + } + + if (vid_delete) + { + printf("%s (%s) %s deleted\n", + package, version_delete, security_delete ? "[s]" : ""); + + if (db->execute(db, NULL, + "DELETE FROM versions WHERE id = ?", + DB_INT, vid_delete) <= 0) + { + fprintf(stderr, "could not delete version '%s' from database\n", + version_delete); + free(version_update); + free(version_delete); + fclose(file); + db->destroy(db); + exit(EXIT_FAILURE); + } + deleted_versions++; + } + free(version_update); + free(version_delete); + } + fclose(file); + db->destroy(db); + + printf("processed %d packages, %d security, %d new packages, " + "%d new versions, %d updated versions, %d deleted versions, " + "%d errored\n", count - 6, vulnerable, new_packages, new_versions, + updated_versions, deleted_versions, errored); +} + +static void do_args(int argc, char *argv[]) +{ + char *filename = NULL, *product = NULL; + bool update = FALSE; + + /* reinit getopt state */ + optind = 0; + + while (TRUE) + { + int c; + + struct option long_opts[] = { + { "help", no_argument, NULL, 'h' }, + { "file", required_argument, NULL, 'f' }, + { "product", required_argument, NULL, 'p' }, + { "update", no_argument, NULL, 'u' }, + { 0,0,0,0 } + }; + + c = getopt_long(argc, argv, "", long_opts, NULL); + switch (c) + { + case EOF: + break; + case 'h': + usage(); + exit(EXIT_SUCCESS); + case 'f': + filename = optarg; + continue; + case 'p': + product = optarg; + continue; + case 'u': + update = TRUE; + continue; + } + break; + } + + if (filename && product) + { + process_packages(filename, product, update); + } + else + { + usage(); + exit(EXIT_FAILURE); + } +} + +int main(int argc, char *argv[]) +{ + /* enable attest debugging hook */ + dbg = pacman_dbg; + openlog("pacman", 0, LOG_DEBUG); + + atexit(cleanup); + + /* initialize library */ + if (!library_init(NULL)) + { + exit(SS_RC_LIBSTRONGSWAN_INTEGRITY); + } + if (!lib->plugins->load(lib->plugins, NULL, + lib->settings->get_str(lib->settings, "attest.load", "sqlite"))) + { + exit(SS_RC_INITIALIZATION_FAILED); + } + do_args(argc, argv); + + exit(EXIT_SUCCESS); +} + |