sec-updater: Import SWID tags of updated packages
authorAndreas Steffen <andreas.steffen@strongswan.org>
Sat, 9 Sep 2017 11:13:28 +0000 (13:13 +0200)
committerAndreas Steffen <andreas.steffen@strongswan.org>
Sat, 9 Sep 2017 18:23:19 +0000 (20:23 +0200)
sec-updater downloads the deb package files from security updates from
a given linux repository and uses the swid_generator command to
derive a SWID tag. The SWID tag is then imported into strongTNC
using the manage.py importswid command.

conf/Makefile.am
conf/options/sec-updater.opt [new file with mode: 0644]
src/sec-updater/sec-updater.8.in
src/sec-updater/sec-updater.c
src/sec-updater/sec-updater.sh

index 87319db..0a4a859 100644 (file)
@@ -25,6 +25,7 @@ options = \
        options/starter.opt \
        options/swanctl.opt \
        options/tnc.opt \
+       options/sec-updater.opt \
        options/sw-collector.opt
 
 plugins = \
diff --git a/conf/options/sec-updater.opt b/conf/options/sec-updater.opt
new file mode 100644 (file)
index 0000000..1162efe
--- /dev/null
@@ -0,0 +1,29 @@
+sec-updater {}
+       Options for the sec-updater tool.
+
+       Options for the sec-updater tool.
+
+sec-updater.database =
+       Global IMV policy database URI. If it contains a password, make sure to
+       adjust the permissions of the config file accordingly.
+
+sec-updater.swid_gen.command = /usr/local/bin/swid_generator
+       SWID generator command to be executed.
+
+sec-updater.swid_gen.tag_creator.name = strongSwan Project
+       Name of the tagCreator entity.
+
+sec-updater.swid_gen.tag_creator.regid = strongswan.org
+       regid of the tagCreator entity.
+
+sec-updater.tnc_manager_command = /var/www/tnc/manager.py
+       strongTNC manage.py command used to import SWID tags.
+
+sec-updater.tmp.deb_file = /tmp/sec-updater.deb
+       Temporary storage for downloaded deb package file.
+
+sec-updater.tmp.tag_file = /tmp/sec-updater.tag
+       Temporary storage for generated SWID tags.
+
+sec-updater.load =
+       Plugins to load in sec-updater tool.
index e58f3b9..f17198f 100644 (file)
@@ -10,7 +10,9 @@ sec-updater \- Extracts security update information of Linux distributions
 .OP \-\-debug level
 .OP \-\-quiet
 .OP \-\-security
-.BI \-\-product " name"
+.BI \-\-os " string"
+.BI \-\-arch " string"
+.BI \-\-uri " uri"
 .BI \-\-file " filename"
 .YS
 .
@@ -44,20 +46,37 @@ Disable debug output to stderr.
 .B "\-s, \-\-security"
 Set when parsing a distributions file with security updates.
 .TP
-.BI "\-p, \-\-product " name
-Name of Linux version as stored in database. eg. "Ubuntu 16.04 x86_64".
+.BI "\-o, \-\-os " string
+Name of operating system (OS). eg. "Ubuntu 16.04".
+.TP
+.BI "\-o, \-\-arch " string
+Name of HW architecture. eg. "x86_64".
+.TP
+.BI "\-o, \-\-uri " uri
+URI where to download deb package from.
 .TP
 .BI "\-f, \-\-file " filename
 Linux package information file to parse.
 .
 .SH "CONFIGURATION"
 .
-The following parameter must be configured in strongswan.conf:
+The following parameters can be configured in strongswan.conf:
 .P
  sec-updater {
-    database = sqlite:///etc/pts/config.db
+   database = sqlite:///etc/pts/config.db
+   swid_gen {
+       command = /usr/local/bin/swid_generator
+       tag_creator {
+          name = strongSwan Project
+          regid = strongswan.org
+       }
+    }
+    tnc_manage_command = /var/www/tnc/manage.py
+    tmp {
+       deb_file = /tmp/sec-updater.deb
+       tag_file = /tmp/sec-updater.tag
+    }
  }
-.P
 .
 .SH "SEE ALSO"
 .
index e6e672d..9d48621 100644 (file)
 #include <utils/debug.h>
 
 #define EXIT_NO_UPDATES                80
+#define TMP_DEB_FILE           "/tmp/sec-updater.deb"
+#define TMP_TAG_FILE           "/tmp/sec-updater.tag"
+#define SWID_GEN_CMD           "/usr/local/bin/swid_generator"
+#define TNC_MANAGE_CMD         "/var/www/tnc/manage.py"
 
 typedef enum sec_update_state_t sec_update_state_t;
 
 enum sec_update_state_t {
        SEC_UPDATE_STATE_BEGIN_PACKAGE,
        SEC_UPDATE_STATE_VERSION,
+       SEC_UPDATE_STATE_FILENAME,
        SEC_UPDATE_STATE_END_PACKAGE
 };
 
@@ -106,21 +111,24 @@ static void usage(void)
        printf("\
 Usage:\n\
   sec-updater --help\n\
-  sec-updater [--debug <level>] [--quiet]  [--security] --product <name> --file <filename>\n\n\
+  sec-updater [--debug <level>] [--quiet]  [--security] --os <string>\n\
+               --arch <string> --uri <uri> --file <filename>\n\n\
   Options:\n\
     --help             print usage information\n\
-    --debug            set debug level\n\
+    --debug <level>    set debug level\n\
     --quiet            suppress debug output to stderr\n\
+    --os <string>      operating system\n\
+    --arch <string>    hw architecture\n\
     --security         set when parsing a file with security updates\n\
-    --product <name>   name of the Linux version as stored in the database\n\
-    --file <filename>  package information file to parse");
-}
+    --file <filename>  package information file to parse\n\
+    --uri <uri>        uri where to download deb package from\n");
+ }
 
 /**
  * Update the package database
  */
 static bool update_database(database_t *db, char *package, char *version,
-                                                       bool security, stats_t *stats)
+                                                       bool security, stats_t *stats, bool *new)
 {
        int pid = 0, vid = 0, sec_flag;
        bool first = TRUE, found = FALSE;
@@ -130,6 +138,9 @@ static bool update_database(database_t *db, char *package, char *version,
        /* increment package count */
        stats->packages++;
 
+       /* set new output variable */
+       *new = FALSE;
+
        /* check if package is already in database */
        e = db->query(db, "SELECT id FROM packages WHERE name = ?",
                                          DB_TEXT, package, DB_INT);
@@ -221,6 +232,7 @@ static bool update_database(database_t *db, char *package, char *version,
                        return FALSE;
                }
                stats->new_versions++;
+               *new = TRUE;
        }
 
        return TRUE;
@@ -229,16 +241,21 @@ static bool update_database(database_t *db, char *package, char *version,
 /**
  * Process a package file and store updates in the database
  */
-static int process_packages(char *filename, char *product, bool security)
+static int process_packages(char *path, char *os, char *arch, char *uri,
+                                                       bool security)
 {
-       char *uri, line[BUF_LEN], *pos, *package = NULL, *version = NULL;
+       char line[BUF_LEN], product[BUF_LEN], command[BUF_LEN];
+       char *db_uri, *download_uri = NULL, *swid_regid, *swid_entity;
+       char *pos, *package = NULL, *version = NULL, *filename = NULL;
+       char *swid_gen_cmd, *tnc_manage_cmd, *tmp_deb_file, *tmp_tag_file;
        sec_update_state_t state;
        enumerator_t *e;
        database_t *db;
-       int pid;
+       int len, pid;
+       chunk_t deb = chunk_empty;
        FILE *file;
        stats_t stats;
-       bool success;
+       bool success, new;
 
        /* initialize statistics */
        memset(&stats, 0x00, sizeof(stats_t));
@@ -247,29 +264,32 @@ static int process_packages(char *filename, char *product, bool security)
        stats.release = time(NULL);
 
        /* opening package file */
-       file = fopen(filename, "r");
+       file = fopen(path, "r");
        if (!file)
        {
-               DBG1(DBG_IMV, "  could not open \"%s\"", filename);
+               DBG1(DBG_IMV, "  could not open \"%s\"", path);
                exit(EXIT_FAILURE);
        }
 
        /* connect package database */
-       uri = lib->settings->get_str(lib->settings, "sec-updater.database", NULL);
-       if (!uri)
+       db_uri = lib->settings->get_str(lib->settings, "sec-updater.database", NULL);
+       if (!db_uri)
        {
                DBG1(DBG_IMV, "database URI sec-updater.database not set");
                fclose(file);
                exit(EXIT_FAILURE);
        }
-       db = lib->db->create(lib->db, uri);
+       db = lib->db->create(lib->db, db_uri);
        if (!db)
        {
-               DBG1(DBG_IMV, "could not connect to database '%s'", uri);
+               DBG1(DBG_IMV, "could not connect to database '%s'", db_uri);
                fclose(file);
                exit(EXIT_FAILURE);
        }
 
+       /* form product name by concatenating os and arch strings */
+       snprintf(product, BUF_LEN, "%s %s", os, arch);
+
        /* check if product is already in database */
        e = db->query(db, "SELECT id FROM products WHERE name = ?",
                                  DB_TEXT, product, DB_INT);
@@ -295,6 +315,22 @@ static int process_packages(char *filename, char *product, bool security)
                stats.product = pid;
        }
 
+       /* get settings for the loop */
+       swid_regid = lib->settings->get_str(lib->settings,
+                                               "sec-updater.swid_gen.tag_creator.regid",
+                                               "strongswan.org");
+       swid_entity = lib->settings->get_str(lib->settings,
+                                               "sec-updater.swid_gen.tag_creator.name",
+                                               "strongSwan Project");
+       swid_gen_cmd = lib->settings->get_str(lib->settings,
+                                               "sec-updater.swid_gen.command", SWID_GEN_CMD);
+       tnc_manage_cmd = lib->settings->get_str(lib->settings,
+                                               "sec-updater.tnc_manage_command", TNC_MANAGE_CMD);
+       tmp_deb_file = lib->settings->get_str(lib->settings,
+                                               "sec-updater.tmp.deb_file", TMP_DEB_FILE);
+       tmp_tag_file = lib->settings->get_str(lib->settings,
+                                               "sec-updater.tmp.tag_file", TMP_TAG_FILE);
+
        state = SEC_UPDATE_STATE_BEGIN_PACKAGE;
 
        while (fgets(line, sizeof(line), file))
@@ -331,7 +367,58 @@ static int process_packages(char *filename, char *product, bool security)
                                if (pos)
                                {
                                        version = strndup(version, pos - version);
-                                       state = SEC_UPDATE_STATE_END_PACKAGE;
+                                       success = update_database(db, package, version, security,
+                                                                                         &stats, &new);
+                                       state = (success && new) ? SEC_UPDATE_STATE_FILENAME :
+                                                                                          SEC_UPDATE_STATE_END_PACKAGE;
+                               }
+                               break;
+                       case SEC_UPDATE_STATE_FILENAME:
+                               pos = strstr(pos, "Filename: ");
+                               if (!pos)
+                               {
+                                       continue;
+                               }
+                               state = SEC_UPDATE_STATE_END_PACKAGE;
+
+                               pos += 10;
+                               filename = pos;
+                               pos = strchr(pos, '\n');
+                               if (!pos)
+                               {
+                                       break;
+                               }
+                               len = pos - filename;
+                               if (asprintf(&download_uri, "%s/%.*s", uri, len, filename) == -1)
+                               {
+                                       break;
+                               }
+
+                               /* retrieve deb package file from linux repository */
+                               if (lib->fetcher->fetch(lib->fetcher, download_uri,
+                                                                                               &deb, FETCH_END) != SUCCESS)
+                               {
+                                       DBG1(DBG_IMV, "     %s failed", download_uri);
+                                       break;
+                               }
+                               DBG1(DBG_IMV, "     %s (%u bytes)", download_uri, deb.len);
+
+                               /* store deb package file to temporary location */
+                               if (!chunk_write(deb, tmp_deb_file, 0022, TRUE))
+                               {
+                                       DBG1(DBG_IMV, "     save to '%s' failed", tmp_deb_file);
+                                       break;
+                               }
+
+                               /* generate SWID tag for downloaded deb package */
+                               snprintf(command, BUF_LEN, "%s swid --full --package-file %s "
+                                                "--regid %s --entity-name '%s' --os '%s' --arch '%s' "
+                                                ">> %s", swid_gen_cmd, tmp_deb_file, swid_regid,
+                                                swid_entity, os, arch, tmp_tag_file);
+                               if (system(command) != 0)
+                               {
+                                       DBG1(DBG_IMV, "     tag generation failed");
+                                       break;
                                }
                                break;
                        case SEC_UPDATE_STATE_END_PACKAGE:
@@ -339,9 +426,12 @@ static int process_packages(char *filename, char *product, bool security)
                                {
                                        continue;
                                }
-                               success = update_database(db, package, version, security, &stats);
                                free(package);
                                free(version);
+                               free(download_uri);
+                               chunk_free(&deb);
+                               package = version = download_uri = NULL;
+
                                if (!success)
                                {
                                        fclose(file);
@@ -351,22 +441,32 @@ static int process_packages(char *filename, char *product, bool security)
                                state = SEC_UPDATE_STATE_BEGIN_PACKAGE;
                }
        }
-       switch (state)
-       {
-               case SEC_UPDATE_STATE_END_PACKAGE:
-                       free(version);
-                       /* fall-through */
-               case SEC_UPDATE_STATE_VERSION:
-                       free(package);
-                       break;
-               default:
-                       break;
-       }
+
+       free(package);
+       free(version);
+       free(download_uri);
        fclose(file);
        db->destroy(db);
 
+       /* import swid tags into strongTNC */
+       if (stats.new_versions > 0)
+       {
+               snprintf(command, BUF_LEN, "%s importswid %s",
+                                tnc_manage_cmd, tmp_tag_file);
+               if (system(command) != 0)
+               {
+                       DBG1(DBG_IMV, "tag import failed");
+               }
+               snprintf(command, BUF_LEN, "rm %s %s",
+                                tmp_deb_file, tmp_tag_file);
+               if (system(command) != 0)
+               {
+                       DBG1(DBG_IMV, "removing temporary files failed");
+               }
+       }
+
        DBG1(DBG_IMV, "processed \"%s\": %d packages, %d new versions, "
-                                 "%d updated versions", filename, stats.packages,
+                                 "%d updated versions", path, stats.packages,
                                   stats.new_versions, stats.updated_versions);
 
        return (stats.new_versions + stats.updated_versions) ?
@@ -375,7 +475,7 @@ static int process_packages(char *filename, char *product, bool security)
 
 static int do_args(int argc, char *argv[])
 {
-       char *filename = NULL, *product = NULL;
+       char *filename = NULL, *arch = NULL, *os = NULL, *uri = NULL;
        bool security = FALSE;
 
        /* reinit getopt state */
@@ -387,15 +487,17 @@ static int do_args(int argc, char *argv[])
 
                struct option long_opts[] = {
                        { "help", no_argument, NULL, 'h' },
+                       { "arch", required_argument, NULL, 'a' },
                        { "debug", required_argument, NULL, 'd' },
                        { "file", required_argument, NULL, 'f' },
-                       { "product", required_argument, NULL, 'p' },
+                       { "os", required_argument, NULL, 'o' },
                        { "quiet", no_argument, NULL, 'q' },
                        { "security", no_argument, NULL, 's' },
+                       { "uri", required_argument, NULL, 'u' },
                        { 0,0,0,0 }
                };
 
-               c = getopt_long(argc, argv, "", long_opts, NULL);
+               c = getopt_long(argc, argv, "ha:d:f:o:qsu:", long_opts, NULL);
                switch (c)
                {
                        case EOF:
@@ -403,14 +505,17 @@ static int do_args(int argc, char *argv[])
                        case 'h':
                                usage();
                                exit(EXIT_SUCCESS);
+                       case 'a':
+                               arch = optarg;
+                               continue;
                        case 'd':
                                debug_level = atoi(optarg);
                                continue;
                        case 'f':
                                filename = optarg;
                                continue;
-                       case 'p':
-                               product = optarg;
+                       case 'o':
+                               os = optarg;
                                continue;
                        case 'q':
                                stderr_quiet = TRUE;
@@ -418,13 +523,16 @@ static int do_args(int argc, char *argv[])
                        case 's':
                                security = TRUE;
                                continue;
+                       case 'u':
+                               uri = optarg;
+                               continue;
                }
                break;
        }
 
-       if (filename && product)
+       if (filename && os && arch && uri)
        {
-               return process_packages(filename, product, security);
+               return process_packages(filename, os, arch, uri, security);
        }
        else
        {
@@ -447,7 +555,8 @@ int main(int argc, char *argv[])
                exit(SS_RC_LIBSTRONGSWAN_INTEGRITY);
        }
        if (!lib->plugins->load(lib->plugins,
-                       lib->settings->get_str(lib->settings, "sec-updater.load", "sqlite")))
+                       lib->settings->get_str(lib->settings, "sec-updater.load",
+                                                                                                 "sqlite curl")))
        {
                exit(SS_RC_INITIALIZATION_FAILED);
        }
index 1177c8b..ca7b898 100755 (executable)
@@ -10,7 +10,7 @@ UBUNTU_ARCH="binary-amd64"
 DEBIAN="http://security.debian.org"
 DEBIAN_VERSIONS="jessie wheezy"
 DEBIAN_DIRS="main contrib non-free"
-DEBIAN_ARCH="binary-amd64"
+DEBIAN_ARCH="binary-amd64 binary-armhf"
 RASPIAN="http://archive.raspberrypi.org/debian"
 RASPIAN_VERSIONS="jessie wheezy"
 RASPIAN_DIRS="main"
@@ -74,66 +74,88 @@ done
 for f in xenial-security/binary-amd64/*
 do
   echo "security: $f"
-  $CMD --product "Ubuntu 16.04 x86_64" --file $f --security >> $CMD_LOG 2>&1
+  $CMD --os "Ubuntu 16.04" --arch "x86_64" --file $f --security \
+       --uri $UBUNTU >> $CMD_LOG 2>&1
   if [ $? -eq 0 ]
   then
     DEL_LOG=0
-    echo $DEL_LOG
   fi
 done
 
 for f in xenial-updates/binary-amd64/*
 do
   echo "updates:  $f"
-  $CMD --product "Ubuntu 16.04 x86_64" --file $f >> $CMD_LOG 2>&1
+  $CMD --os "Ubuntu 16.04" --arch "x86_64" --file $f \
+       --uri $UBUNTU >> $CMD_LOG 2>&1
   if [ $? -eq 0 ]
   then
     DEL_LOG=0
-    echo $DEL_LOG
   fi
 done
 
 for f in jessie-updates/binary-amd64/*
 do
   echo "security: $f"
-  $CMD --product "Debian 8.0 x86_64" --file $f --security >> $CMD_LOG 2>&1
+  $CMD --os "Debian 8.0" --arch "x86_64" --file $f --security \
+       --uri $DEBIAN >> $CMD_LOG 2>&1
   if [ $? -eq 0 ]
   then
     DEL_LOG=0
-    echo $DEL_LOG
   fi
 done
 
 for f in wheezy-updates/binary-amd64/*
 do
   echo "security: $f"
-  $CMD --product "Debian 7.0 x86_64" --file $f --security >> $CMD_LOG 2>&1
+  $CMD --os "Debian 7.0" --arch "x86_64" --file $f --security \
+       --uri $DEBIAN >> $CMD_LOG 2>&1
+  if [ $? -eq 0 ]
+  then
+    DEL_LOG=0
+  fi
+done
+
+for f in jessie-updates/binary-armhf/*
+do
+  echo "security: $f"
+  $CMD --os "Debian 8.0" --arch "armhf" --file $f --security \
+       --uri $DEBIAN >> $CMD_LOG 2>&1
+  if [ $? -eq 0 ]
+  then
+    DEL_LOG=0
+  fi
+done
+
+for f in wheezy-updates/binary-armhf/*
+do
+  echo "security: $f"
+  $CMD --os "Debian 7.0" --arch "armhf" --file $f --security \
+       --uri $DEBIAN >> $CMD_LOG 2>&1
   if [ $? -eq 0 ]
   then
     DEL_LOG=0
-    echo $DEL_LOG
   fi
 done
 
 for f in jessie-raspian/binary-armhf/*
 do
   echo "security: $f"
-  $CMD --product "Debian 8.0 armv7l" --file $f --security >> $CMD_LOG 2>&1
+  $CMD --os "Debian 8.0" --arch "armv7l" --file $f --security \
+       --uri $RASPIAN >> $CMD_LOG 2>&1
   if [ $? -eq 0 ]
   then
     DEL_LOG=0
-    echo $DEL_LOG
   fi
 done
 
 for f in wheezy-raspian/binary-armhf/*
 do
   echo "security: $f"
-  $CMD --product "Debian 7.11 armv7l" --file $f --security >> $CMD_LOG 2>&1
+  $CMD --os "Debian 7.11" --arch "armv7l" --file $f --security \
+       --uri $RASPIAN >> $CMD_LOG 2>&1
   if [ $? -eq 0 ]
   then
     DEL_LOG=0
-    echo $DEL_LOG
   fi
 done