Moved BIOS and IMA measurement lists into classes of their own
authorAndreas Steffen <andreas.steffen@strongswan.org>
Thu, 1 May 2014 20:13:06 +0000 (22:13 +0200)
committerAndreas Steffen <andreas.steffen@strongswan.org>
Fri, 2 May 2014 05:30:17 +0000 (07:30 +0200)
src/libpts/Makefile.am
src/libpts/pts/components/ita/ita_comp_ima.c
src/libpts/pts/pts_ima_bios_list.c [new file with mode: 0644]
src/libpts/pts/pts_ima_bios_list.h [new file with mode: 0644]
src/libpts/pts/pts_ima_event_list.c [new file with mode: 0644]
src/libpts/pts/pts_ima_event_list.h [new file with mode: 0644]

index 48bb46c..6bd3a58 100644 (file)
@@ -31,6 +31,8 @@ libpts_la_SOURCES = \
        pts/pts_file_meas.h pts/pts_file_meas.c \
        pts/pts_file_meta.h pts/pts_file_meta.c \
        pts/pts_file_type.h pts/pts_file_type.c \
+       pts/pts_ima_bios_list.h pts/pts_ima_bios_list.c \
+       pts/pts_ima_event_list.h pts/pts_ima_event_list.c \
        pts/pts_meas_algo.h pts/pts_meas_algo.c \
        pts/components/pts_component.h \
        pts/components/pts_component_manager.h pts/components/pts_component_manager.c \
index b5a91ca..7a7f6b9 100644 (file)
 
 #include "libpts.h"
 #include "pts/pts_pcr.h"
+#include "pts/pts_ima_bios_list.h"
+#include "pts/pts_ima_event_list.h"
 #include "pts/components/pts_component.h"
 
 #include <utils/debug.h>
 #include <crypto/hashers/hasher.h>
 #include <pen/pen.h>
 
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <errno.h>
-
 #define SECURITY_DIR                           "/sys/kernel/security/"
 #define IMA_BIOS_MEASUREMENTS          SECURITY_DIR "tpm0/binary_bios_measurements"
 #define IMA_RUNTIME_MEASUREMENTS       SECURITY_DIR "ima/binary_runtime_measurements"
-#define IMA_PCR                                                10
-#define IMA_TYPE_LEN                           3
-#define IMA_NG_TYPE_LEN                                6
-#define IMA_TYPE_LEN_MAX                       10
-#define IMA_ALGO_LEN_MIN                       5
-#define IMA_ALGO_LEN_MAX                       8
-#define IMA_ALGO_DIGEST_LEN_MAX                IMA_ALGO_LEN_MAX + HASH_SIZE_SHA512
 #define IMA_FILENAME_LEN_MAX           255
 
 typedef struct pts_ita_comp_ima_t pts_ita_comp_ima_t;
-typedef struct bios_entry_t bios_entry_t;
-typedef struct ima_entry_t ima_entry_t;
 typedef enum ima_state_t ima_state_t;
 
 enum ima_state_t {
@@ -119,12 +106,12 @@ struct pts_ita_comp_ima_t {
        /**
      * IMA BIOS measurements
         */
-       linked_list_t *bios_list;
+       pts_ima_bios_list_t *bios_list;
 
        /**
      * IMA runtime file measurements
         */
-       linked_list_t *ima_list;
+       pts_ima_event_list_t *ima_list;
 
        /**
         * Whether to send pcr_before and pcr_after info
@@ -132,9 +119,9 @@ struct pts_ita_comp_ima_t {
        bool pcr_info;
 
        /**
-        * IMA measurement time
+        * Creation time of measurement
         */
-       time_t measurement_time;
+       time_t creation_time;
 
        /**
         * IMA state machine
@@ -174,333 +161,6 @@ struct pts_ita_comp_ima_t {
 };
 
 /**
- * Linux IMA BIOS measurement entry
- */
-struct bios_entry_t {
-
-       /**
-        * PCR register
-        */
-       uint32_t pcr;
-
-       /**
-        * SHA1 measurement hash
-        */
-       chunk_t measurement;
-};
-
-/**
- * Linux IMA runtime file measurement entry
- */
-struct ima_entry_t {
-
-       /**
-        * SHA1 measurement hash
-        */
-       chunk_t measurement;
-
-       /**
-        * IMA-NG hash algorithm name or NULL
-        */
-       char *algo;
-
-       /**
-        * IMA-NG eventname or IMA filename
-        */
-       char *filename;
-};
-
-/**
- * Free a bios_entry_t object
- */
-static void free_bios_entry(bios_entry_t *this)
-{
-       free(this->measurement.ptr);
-       free(this);
-}
-
-/**
- * Free an ima_entry_t object
- */
-static void free_ima_entry(ima_entry_t *this)
-{
-       free(this->measurement.ptr);
-       free(this->algo);
-       free(this->filename);
-       free(this);
-}
-
-/**
- * Load a PCR measurement file and determine the creation date
- */
-static bool load_bios_measurements(char *file, linked_list_t *list,
-                                                                  time_t *created)
-{
-       uint32_t pcr, num, len;
-       bios_entry_t *entry;
-       struct stat st;
-       ssize_t res;
-       int fd;
-
-       fd = open(file, O_RDONLY);
-       if (fd == -1)
-       {
-               DBG1(DBG_PTS, "opening '%s' failed: %s", file, strerror(errno));
-               return FALSE;
-       }
-
-       if (fstat(fd, &st) == -1)
-       {
-               DBG1(DBG_PTS, "getting statistics of '%s' failed: %s", file,
-                        strerror(errno));
-               close(fd);
-               return FALSE;
-       }
-       *created = st.st_ctime;
-
-       while (TRUE)
-       {
-               res = read(fd, &pcr, 4);
-               if (res == 0)
-               {
-                       DBG2(DBG_PTS, "loaded bios measurements '%s' (%d entries)",
-                                file, list->get_count(list));
-                       close(fd);
-                       return TRUE;
-               }
-
-               entry = malloc_thing(bios_entry_t);
-               entry->pcr = pcr;
-               entry->measurement = chunk_alloc(HASH_SIZE_SHA1);
-
-               if (res != 4)
-               {
-                       break;
-               }
-               if (read(fd, &num, 4) != 4)
-               {
-                       break;
-               }
-               if (read(fd, entry->measurement.ptr, HASH_SIZE_SHA1) != HASH_SIZE_SHA1)
-               {
-                       break;
-               }
-               if (read(fd, &len, 4) != 4)
-               {
-                       break;
-               }
-               if (lseek(fd, len, SEEK_CUR) == -1)
-               {
-                       break;
-               }
-               list->insert_last(list, entry);
-       }
-
-       DBG1(DBG_PTS, "loading bios measurements '%s' failed: %s", file,
-                strerror(errno));
-       free_bios_entry(entry);
-       close(fd);
-       return FALSE;
-}
-
-/**
- * Load an IMA runtime measurement file and determine the creation and
- * update dates
- */
-static bool load_runtime_measurements(char *file, linked_list_t *list,
-                                                                         time_t *created)
-{
-       ima_entry_t *entry;
-       uint32_t pcr, type_len, filename_len;
-       uint32_t eventdata_len, algo_digest_len, algo_len;
-       bool ima_ng;
-       char type[IMA_TYPE_LEN_MAX];
-       char algo_digest[IMA_ALGO_DIGEST_LEN_MAX];
-       char *pos, *error = "";
-       struct stat st;
-       ssize_t res;
-       int fd;
-
-       fd = open(file, O_RDONLY);
-       if (fd == -1)
-       {
-               DBG1(DBG_PTS, "opening '%s' failed: %s", file, strerror(errno));
-               return TRUE;
-       }
-
-       if (fstat(fd, &st) == -1)
-       {
-               DBG1(DBG_PTS, "getting statistics of '%s' failed: %s", file,
-                        strerror(errno));
-               close(fd);
-               return FALSE;
-       }
-       *created = st.st_ctime;
-
-       while (TRUE)
-       {
-               /* read 32 bit PCR number in host order */
-               res = read(fd, &pcr, 4);
-
-               /* exit if no more measurement data is available */
-               if (res == 0)
-               {
-                       DBG2(DBG_PTS, "loaded ima measurements '%s' (%d entries)",
-                                file, list->get_count(list));
-                       close(fd);
-                       return TRUE;
-               }
-
-               /* create and initialize new IMA entry */
-               entry = malloc_thing(ima_entry_t);
-               entry->measurement = chunk_alloc(HASH_SIZE_SHA1);
-               entry->algo = NULL;
-               entry->filename = NULL;
-
-               if (res != 4 || pcr != IMA_PCR)
-               {
-                       error = "invalid IMA PCR field";
-                       break;
-               }
-
-               /* read 20 byte SHA-1 measurement digest */
-               if (read(fd, entry->measurement.ptr, HASH_SIZE_SHA1) != HASH_SIZE_SHA1)
-               {
-                       error = "invalid SHA-1 digest field";
-                       break;
-               }
-
-               /* read 32 bit length of IMA type string in host order */
-               if (read(fd, &type_len, 4) != 4 || type_len > IMA_TYPE_LEN_MAX)
-               {
-                       error = "invalid IMA type field length";
-                       break;
-               }
-
-               /* read and interpret IMA type string */
-               if (read(fd, type, type_len) != type_len)
-               {
-                       error = "invalid IMA type field";
-                       break;
-               }
-               if (type_len == IMA_NG_TYPE_LEN &&
-                       memeq(type, "ima-ng", IMA_NG_TYPE_LEN))
-               {
-                       ima_ng = TRUE;
-               }
-               else if (type_len == IMA_TYPE_LEN &&
-                                memeq(type, "ima", IMA_TYPE_LEN))
-               {
-                       ima_ng = FALSE;
-               }
-               else
-               {
-                       error = "unknown IMA type";
-                       break;
-               }
-
-               if (ima_ng)
-               {
-                       /* read the 32 bit length of the event data in host order */
-                       if (read(fd, &eventdata_len, 4) != 4 || eventdata_len < 4)
-                       {
-                               error = "invalid event data field length";
-                               break;
-                       }
-
-                       /* read the 32 bit length of the algo_digest string in host order */
-                       if (read(fd, &algo_digest_len, 4) != 4 ||
-                               algo_digest_len > IMA_ALGO_DIGEST_LEN_MAX ||
-                               eventdata_len < 4 + algo_digest_len + 4)
-                       {
-                               error = "invalid digest_with_algo field length";
-                               break;
-                       }
-
-                       /* read the IMA algo_digest string */
-                       if (read(fd, algo_digest, algo_digest_len) != algo_digest_len)
-                       {
-                               error = "invalid digest_with_algo field";
-                               break;
-                       }
-
-                       /* extract the hash algorithm name */
-                       pos = strchr(algo_digest, '\0');
-                       if (!pos)
-                       {
-                               error = "no algo field";
-                               break;
-                       }
-                       algo_len = pos - algo_digest + 1;
-
-                       if (algo_len > IMA_ALGO_LEN_MAX ||
-                               algo_len < IMA_ALGO_LEN_MIN || *(pos - 1) != ':')
-                       {
-                               error = "invalid algo field";
-                               break;
-                       }
-
-                       /* copy and store the hash algorithm name */
-                       entry->algo = malloc(algo_len);
-                       memcpy(entry->algo, algo_digest, algo_len);
-
-                       /* read the 32 bit length of the file name in host order */
-                       if (read(fd, &filename_len, 4) != 4 ||
-                               eventdata_len != 4 + algo_digest_len + 4 + filename_len)
-                       {
-                               error = "invalid filename field length";
-                               break;
-                       }
-
-                       /* allocate memory for the file name */
-                       entry->filename = malloc(filename_len);
-
-                       /* read file name */
-                       if (read(fd, entry->filename, filename_len) != filename_len)
-                       {
-                               error = "invalid filename field";
-                               break;
-                       }
-               }
-               else
-               {
-                       /* skip SHA-1 digest of the file content */
-                       if (lseek(fd, HASH_SIZE_SHA1, SEEK_CUR) == -1)
-                       {
-                               break;
-                       }
-
-                       /* read the 32 bit length of the file name in host order */
-                       if (read(fd, &filename_len, 4) != 4)
-                       {
-                               error = "invalid filename field length";
-                               break;
-                       }
-
-                       /* allocate memory for the file name */
-                       entry->filename = malloc(filename_len + 1);
-
-                       /* read file name */
-                       if (read(fd, entry->filename, filename_len) != filename_len)
-                       {
-                               error = "invalid filename field";
-                               break;
-                       }
-
-                       /* terminate the file name with a nul character */
-                       entry->filename[filename_len] = '\0';
-               }
-
-               list->insert_last(list, entry);
-       }
-
-       DBG1(DBG_PTS, "loading ima measurements '%s' failed: %s", file, error);
-       free_ima_entry(entry);
-       close(fd);
-       return FALSE;
-}
-
-/**
  * Extend measurement into PCR and create evidence
  */
 static pts_comp_evidence_t* extend_pcr(pts_ita_comp_ima_t* this,
@@ -531,7 +191,7 @@ static pts_comp_evidence_t* extend_pcr(pts_ita_comp_ima_t* this,
        name = this->name->clone(this->name);
        name->set_qualifier(name, qualifier);
        evidence = pts_comp_evidence_create(name, this->depth, pcr, hash_algo,
-                                       pcr_transform, this->measurement_time, measurement);
+                                               pcr_transform, this->creation_time, measurement);
        if (this->pcr_info)
        {
                pcr_after =chunk_clone(pcrs->get(pcrs, pcr));
@@ -681,12 +341,12 @@ METHOD(pts_component_t, measure, status_t,
        pts_ita_comp_ima_t *this, uint8_t qualifier, pts_t *pts,
        pts_comp_evidence_t **evidence)
 {
-       bios_entry_t *bios_entry;
-       ima_entry_t *ima_entry;
        pts_pcr_t *pcrs;
        pts_comp_evidence_t *evid = NULL;
        size_t algo_len, name_len;
-       char *uri;
+       chunk_t measurement;
+       char *uri, *algo, *name;
+       uint32_t pcr;
        status_t status;
 
        pcrs = pts->get_pcrs(pts);
@@ -697,25 +357,25 @@ METHOD(pts_component_t, measure, status_t,
                switch (this->state)
                {
                        case IMA_STATE_INIT:
-                               if (!load_bios_measurements(IMA_BIOS_MEASUREMENTS,
-                                       this->bios_list, &this->measurement_time))
+                               this->bios_list = pts_ima_bios_list_create(
+                                                                                               IMA_BIOS_MEASUREMENTS);
+                               if (!this->bios_list)
                                {
                                        return FAILED;
                                }
+                               this->creation_time = this->bios_list->get_time(this->bios_list);
                                this->bios_count = this->bios_list->get_count(this->bios_list);
                                this->state = IMA_STATE_BIOS;
                                /* fall through to next state */
                        case IMA_STATE_BIOS:
-                               status = this->bios_list->remove_first(this->bios_list,
-                                                                                                         (void**)&bios_entry);
+                               status = this->bios_list->get_next(this->bios_list, &pcr,
+                                                                                              &measurement);
                                if (status != SUCCESS)
                                {
                                        DBG1(DBG_PTS, "could not retrieve bios measurement entry");
                                        return status;
                                }
-                               evid = extend_pcr(this, qualifier, pcrs, bios_entry->pcr,
-                                                                 bios_entry->measurement);
-                               free(bios_entry);
+                               evid = extend_pcr(this, qualifier, pcrs, pcr, measurement);
 
                                this->state = this->bios_list->get_count(this->bios_list) ?
                                                                                IMA_STATE_BIOS : IMA_STATE_INIT;
@@ -730,17 +390,20 @@ METHOD(pts_component_t, measure, status_t,
                switch (this->state)
                {
                        case IMA_STATE_INIT:
-                               if (!load_runtime_measurements(IMA_RUNTIME_MEASUREMENTS,
-                                                               this->ima_list, &this->measurement_time))
+                               this->ima_list = pts_ima_event_list_create(
+                                                                                               IMA_RUNTIME_MEASUREMENTS);
+                               if (!this->ima_list)
                                {
                                        return FAILED;
                                }
+                               this->creation_time = this->ima_list->get_time(this->ima_list);
+                               this->count = this->ima_list->get_count(this->ima_list);
                                this->state = IMA_STATE_BOOT_AGGREGATE;
                                /* fall through to next state */
                        case IMA_STATE_BOOT_AGGREGATE:
                        case IMA_STATE_RUNTIME:
-                               status = this->ima_list->remove_first(this->ima_list,
-                                                                                                        (void**)&ima_entry);
+                               status = this->ima_list->get_next(this->ima_list, &measurement,
+                                                                                                 &algo, &name);
                                if (status != SUCCESS)
                                {
                                        DBG1(DBG_PTS, "could not retrieve ima measurement entry");
@@ -748,35 +411,33 @@ METHOD(pts_component_t, measure, status_t,
                                }
                                if (this->state == IMA_STATE_BOOT_AGGREGATE && this->bios_count)
                                {
-                                       if (!check_boot_aggregate(pcrs, ima_entry->measurement,
-                                                                                                       ima_entry->algo))
+                                       if (!check_boot_aggregate(pcrs, measurement, algo))
                                        {
                                                return FAILED;
                                        }
                                }
                                evid = extend_pcr(this, qualifier, pcrs, IMA_PCR,
-                                                                 ima_entry->measurement);
+                                                                 measurement);
                                if (evid)
                                {
-                                       if (ima_entry->algo)
+                                       if (algo)
                                        {
-                                               algo_len = strlen(ima_entry->algo);
-                                               name_len = strlen(ima_entry->filename);
+                                               algo_len = strlen(algo);
+                                               name_len = strlen(name);
                                                uri = malloc(algo_len + name_len + 1);
-                                               memcpy(uri, ima_entry->algo, algo_len);
-                                               strcpy(uri + algo_len, ima_entry->filename);
+                                               memcpy(uri, algo, algo_len);
+                                               strcpy(uri + algo_len, name);
                                        }
                                        else
                                        {
-                                               uri = strdup(ima_entry->filename);
+                                               uri = strdup(name);
                                        }
                                        evid->set_validation(evid, PTS_COMP_EVID_VALIDATION_PASSED,
                                                                                           uri);
                                        free(uri);
                                }
-                               free(ima_entry->filename);
-                               free(ima_entry->algo);
-                               free(ima_entry);
+                               free(name);
+                               free(algo);
 
                                this->state = this->ima_list->get_count(this->ima_list) ?
                                                                        IMA_STATE_RUNTIME : IMA_STATE_END;
@@ -867,14 +528,14 @@ METHOD(pts_component_t, verify, status_t,
        pts_meas_algorithms_t algo;
        pts_pcr_transform_t transform;
        pts_pcr_t *pcrs;
-       time_t measurement_time;
+       time_t creation_time;
        chunk_t measurement, pcr_before, pcr_after;
        status_t status = NOT_FOUND;
 
        this->aik_id = pts->get_aik_id(pts);
        pcrs = pts->get_pcrs(pts);
        measurement = evidence->get_measurement(evidence, &pcr, &algo, &transform,
-                                                                                       &measurement_time);
+                                                                                       &creation_time);
 
        if (qualifier == (PTS_ITA_QUALIFIER_FLAG_KERNEL |
                                          PTS_ITA_QUALIFIER_TYPE_TRUSTED))
@@ -1212,10 +873,8 @@ METHOD(pts_component_t, destroy, void,
                        DBG1(DBG_PTS, "deleted registered boot aggregate evidence "
                                                  "measurement");
                }
-               this->bios_list->destroy_function(this->bios_list,
-                                                                                (void *)free_bios_entry);
-               this->ima_list->destroy_function(this->ima_list,
-                                                                                (void *)free_ima_entry);
+               DESTROY_IF(this->bios_list);
+               DESTROY_IF(this->ima_list);
                this->name->destroy(this->name);
                
                free(this);
@@ -1245,8 +904,6 @@ pts_component_t *pts_ita_comp_ima_create(uint32_t depth,
                                                                                  PTS_QUALIFIER_UNKNOWN),
                .depth = depth,
                .pts_db = pts_db,
-               .bios_list = linked_list_create(),
-               .ima_list = linked_list_create(),
                .pcr_info = lib->settings->get_bool(lib->settings,
                                                "%s.plugins.imc-attestation.pcr_info", TRUE, lib->ns),
                .ref = 1,
diff --git a/src/libpts/pts/pts_ima_bios_list.c b/src/libpts/pts/pts_ima_bios_list.c
new file mode 100644 (file)
index 0000000..18c41f2
--- /dev/null
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2011-2014 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 "pts_ima_bios_list.h"
+
+#include <utils/debug.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+
+typedef struct private_pts_ima_bios_list_t private_pts_ima_bios_list_t;
+typedef struct bios_entry_t bios_entry_t;
+
+/**
+ * Private data of a pts_ima_bios_list_t object.
+ *
+ */
+struct private_pts_ima_bios_list_t {
+
+       /**
+        * Public pts_ima_bios_list_t interface.
+        */
+       pts_ima_bios_list_t public;
+
+       /**
+        * List of BIOS measurement entries
+        */
+       linked_list_t *list;
+
+       /**
+        * Time when BIOS measurements were taken
+        */
+       time_t creation_time;
+
+};
+
+/**
+ * Linux IMA BIOS measurement entry
+ */
+struct bios_entry_t {
+
+       /**
+        * PCR register
+        */
+       uint32_t pcr;
+
+       /**
+        * SHA1 measurement hash
+        */
+       chunk_t measurement;
+};
+
+/**
+ * Free a bios_entry_t object
+ */
+static void free_bios_entry(bios_entry_t *this)
+{
+       free(this->measurement.ptr);
+       free(this);
+}
+
+METHOD(pts_ima_bios_list_t, get_time, time_t,
+       private_pts_ima_bios_list_t *this)
+{
+       return this->creation_time;
+}
+
+METHOD(pts_ima_bios_list_t, get_count, int,
+       private_pts_ima_bios_list_t *this)
+{
+       return this->list->get_count(this->list);
+}
+
+METHOD(pts_ima_bios_list_t, get_next, status_t,
+       private_pts_ima_bios_list_t *this, uint32_t *pcr, chunk_t *measurement)
+{
+       bios_entry_t *entry;
+       status_t status;
+
+       status = this->list->remove_first(this->list, (void**)&entry);
+       *pcr = entry->pcr;
+       *measurement = entry->measurement;
+       free(entry);
+
+       return status;
+}
+
+METHOD(pts_ima_bios_list_t, destroy, void,
+       private_pts_ima_bios_list_t *this)
+{
+       this->list->destroy_function(this->list, (void *)free_bios_entry);
+       free(this);
+}
+
+/**
+ * See header
+ */
+pts_ima_bios_list_t* pts_ima_bios_list_create(char *file)
+{
+       private_pts_ima_bios_list_t *this;
+       uint32_t pcr, num, len;
+       bios_entry_t *entry;
+       struct stat st;
+       ssize_t res;
+       int fd;
+
+       fd = open(file, O_RDONLY);
+       if (fd == -1)
+       {
+               DBG1(DBG_PTS, "opening '%s' failed: %s", file, strerror(errno));
+               return NULL;
+       }
+
+       if (fstat(fd, &st) == -1)
+       {
+               DBG1(DBG_PTS, "getting statistics of '%s' failed: %s", file,
+                        strerror(errno));
+               close(fd);
+               return FALSE;
+       }
+
+       INIT(this,
+               .public = {
+                       .get_time = _get_time,
+                       .get_count = _get_count,
+                       .get_next = _get_next,
+                       .destroy = _destroy,
+               },
+               .creation_time = st.st_ctime,
+               .list = linked_list_create(),
+       );
+
+       while (TRUE)
+       {
+               res = read(fd, &pcr, 4);
+               if (res == 0)
+               {
+                       DBG2(DBG_PTS, "loaded bios measurements '%s' (%d entries)",
+                                file, this->list->get_count(this->list));
+                       close(fd);
+                       return &this->public;
+               }
+
+               entry = malloc_thing(bios_entry_t);
+               entry->pcr = pcr;
+               entry->measurement = chunk_alloc(HASH_SIZE_SHA1);
+
+               if (res != 4)
+               {
+                       break;
+               }
+               if (read(fd, &num, 4) != 4)
+               {
+                       break;
+               }
+               if (read(fd, entry->measurement.ptr, HASH_SIZE_SHA1) != HASH_SIZE_SHA1)
+               {
+                       break;
+               }
+               if (read(fd, &len, 4) != 4)
+               {
+                       break;
+               }
+               if (lseek(fd, len, SEEK_CUR) == -1)
+               {
+                       break;
+               }
+               this->list->insert_last(this->list, entry);
+       }
+
+       DBG1(DBG_PTS, "loading bios measurements '%s' failed: %s", file,
+                strerror(errno));
+       free_bios_entry(entry);
+       close(fd);
+       destroy(this);
+
+       return NULL;
+}
diff --git a/src/libpts/pts/pts_ima_bios_list.h b/src/libpts/pts/pts_ima_bios_list.h
new file mode 100644 (file)
index 0000000..ad162e1
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2014 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 pts_ima_bios_list pts_ima_bios_list
+ * @{ @ingroup pts
+ */
+
+#ifndef PTS_IMA_BIOS_LIST_H_
+#define PTS_IMA_BIOS_LIST_H_
+
+#include <time.h>
+
+#include <library.h>
+
+typedef struct pts_ima_bios_list_t pts_ima_bios_list_t;
+
+/**
+ * Class retrieving Linux IMA BIOS measurements
+ *
+ */
+struct pts_ima_bios_list_t {
+
+       /**
+        * Get the time the BIOS measurements were taken
+        *
+        * @return                              Measurement time
+        */
+       time_t (*get_time)(pts_ima_bios_list_t *this);
+
+       /**
+        * Get the number of non-processed BIOS measurements
+        *
+        * @return                              Number of measurements left
+        */
+       int (*get_count)(pts_ima_bios_list_t *this);
+
+       /**
+        * Get the next BIOS measurement and remove it from the list
+        *
+        * @param pcr                   PCR where the measurement was extended into
+        * @param measurement   Measurement hash
+        * @return                              Return code
+        */
+       status_t (*get_next)(pts_ima_bios_list_t *this, uint32_t *pcr,
+                                                                                                       chunk_t *measurement);
+
+       /**
+        * Destroys a pts_ima_bios_list_t object.
+        */
+       void (*destroy)(pts_ima_bios_list_t *this);
+
+};
+
+/**
+ * Create a PTS IMA BIOS measurement object
+ *
+ * @param file                         Pathname pointing to the BIOS measurements
+ */
+pts_ima_bios_list_t* pts_ima_bios_list_create(char *file);
+
+#endif /** PTS_IMA_BIOS_LIST_H_ @}*/
diff --git a/src/libpts/pts/pts_ima_event_list.c b/src/libpts/pts/pts_ima_event_list.c
new file mode 100644 (file)
index 0000000..a237451
--- /dev/null
@@ -0,0 +1,330 @@
+/*
+ * Copyright (C) 2011-2014 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 "pts_ima_event_list.h"
+
+#include <utils/debug.h>
+#include <crypto/hashers/hasher.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+
+typedef struct private_pts_ima_event_list_t private_pts_ima_event_list_t;
+typedef struct event_entry_t event_entry_t;
+
+#define IMA_TYPE_LEN                           3
+#define IMA_NG_TYPE_LEN                                6
+#define IMA_TYPE_LEN_MAX                       10
+#define IMA_ALGO_DIGEST_LEN_MAX                IMA_ALGO_LEN_MAX + HASH_SIZE_SHA512
+
+/**
+ * Private data of a pts_ima_event_list_t object.
+ *
+ */
+struct private_pts_ima_event_list_t {
+
+       /**
+        * Public pts_ima_event_list_t interface.
+        */
+       pts_ima_event_list_t public;
+
+       /**
+        * List of BIOS measurement entries
+        */
+       linked_list_t *list;
+
+       /**
+        * Time when IMA runtime file measurements were taken
+        */
+       time_t creation_time;
+
+};
+
+/**
+ * Linux IMA runtime file measurement entry
+ */
+struct event_entry_t {
+
+       /**
+        * SHA1 measurement hash
+        */
+       chunk_t measurement;
+
+       /**
+        * IMA-NG hash algorithm name or NULL
+        */
+       char *algo;
+
+       /**
+        * IMA-NG eventname or IMA filename
+        */
+       char *name;
+};
+
+/**
+ * Free an ima_event_t object
+ */
+static void free_event_entry(event_entry_t *this)
+{
+       free(this->measurement.ptr);
+       free(this->algo);
+       free(this->name);
+       free(this);
+}
+
+METHOD(pts_ima_event_list_t, get_time, time_t,
+       private_pts_ima_event_list_t *this)
+{
+       return this->creation_time;
+}
+
+METHOD(pts_ima_event_list_t, get_count, int,
+       private_pts_ima_event_list_t *this)
+{
+       return this->list->get_count(this->list);
+}
+
+METHOD(pts_ima_event_list_t, get_next, status_t,
+       private_pts_ima_event_list_t *this, chunk_t *measurement, char **algo,
+       char **name)
+{
+       event_entry_t *entry;
+       status_t status;
+
+       status = this->list->remove_first(this->list, (void**)&entry);
+       *measurement = entry->measurement;
+       *algo = entry->algo;
+       *name = entry->name;
+       free(entry);
+
+       return status;
+}
+
+METHOD(pts_ima_event_list_t, destroy, void,
+       private_pts_ima_event_list_t *this)
+{
+       this->list->destroy_function(this->list, (void *)free_event_entry);
+       free(this);
+}
+
+/**
+ * See header
+ */
+pts_ima_event_list_t* pts_ima_event_list_create(char *file)
+{
+       private_pts_ima_event_list_t *this;
+       event_entry_t *entry;
+       uint32_t pcr, type_len, name_len, eventdata_len, algo_digest_len, algo_len;
+       char type[IMA_TYPE_LEN_MAX];
+       char algo_digest[IMA_ALGO_DIGEST_LEN_MAX];
+       char *pos, *error = "";
+       struct stat st;
+       ssize_t res;
+       bool ima_ng;
+       int fd;
+
+       fd = open(file, O_RDONLY);
+       if (fd == -1)
+       {
+               DBG1(DBG_PTS, "opening '%s' failed: %s", file, strerror(errno));
+               return NULL;
+       }
+
+       if (fstat(fd, &st) == -1)
+       {
+               DBG1(DBG_PTS, "getting statistics of '%s' failed: %s", file,
+                        strerror(errno));
+               close(fd);
+               return NULL;
+       }
+
+       INIT(this,
+               .public = {
+                       .get_time = _get_time,
+                       .get_count = _get_count,
+                       .get_next = _get_next,
+                       .destroy = _destroy,
+               },
+               .creation_time = st.st_ctime,
+               .list = linked_list_create(),
+       );
+
+       while (TRUE)
+       {
+               /* read 32 bit PCR number in host order */
+               res = read(fd, &pcr, 4);
+
+               /* exit if no more measurement data is available */
+               if (res == 0)
+               {
+                       DBG2(DBG_PTS, "loaded ima measurements '%s' (%d entries)",
+                                file, this->list->get_count(this->list));
+                       close(fd);
+                       return &this->public;
+               }
+
+               /* create and initialize new IMA entry */
+               entry = malloc_thing(event_entry_t);
+               entry->measurement = chunk_alloc(HASH_SIZE_SHA1);
+               entry->algo = NULL;
+               entry->name = NULL;
+
+               if (res != 4 || pcr != IMA_PCR)
+               {
+                       error = "invalid IMA PCR field";
+                       break;
+               }
+
+               /* read 20 byte SHA-1 measurement digest */
+               if (read(fd, entry->measurement.ptr, HASH_SIZE_SHA1) != HASH_SIZE_SHA1)
+               {
+                       error = "invalid SHA-1 digest field";
+                       break;
+               }
+
+               /* read 32 bit length of IMA type string in host order */
+               if (read(fd, &type_len, 4) != 4 || type_len > IMA_TYPE_LEN_MAX)
+               {
+                       error = "invalid IMA type field length";
+                       break;
+               }
+
+               /* read and interpret IMA type string */
+               if (read(fd, type, type_len) != type_len)
+               {
+                       error = "invalid IMA type field";
+                       break;
+               }
+               if (type_len == IMA_NG_TYPE_LEN &&
+                       memeq(type, "ima-ng", IMA_NG_TYPE_LEN))
+               {
+                       ima_ng = TRUE;
+               }
+               else if (type_len == IMA_TYPE_LEN &&
+                                memeq(type, "ima", IMA_TYPE_LEN))
+               {
+                       ima_ng = FALSE;
+               }
+               else
+               {
+                       error = "unknown IMA type";
+                       break;
+               }
+
+               if (ima_ng)
+               {
+                       /* read the 32 bit length of the event data in host order */
+                       if (read(fd, &eventdata_len, 4) != 4 || eventdata_len < 4)
+                       {
+                               error = "invalid event data field length";
+                               break;
+                       }
+
+                       /* read the 32 bit length of the algo_digest string in host order */
+                       if (read(fd, &algo_digest_len, 4) != 4 ||
+                               algo_digest_len > IMA_ALGO_DIGEST_LEN_MAX ||
+                               eventdata_len < 4 + algo_digest_len + 4)
+                       {
+                               error = "invalid digest_with_algo field length";
+                               break;
+                       }
+
+                       /* read the IMA algo_digest string */
+                       if (read(fd, algo_digest, algo_digest_len) != algo_digest_len)
+                       {
+                               error = "invalid digest_with_algo field";
+                               break;
+                       }
+
+                       /* extract the hash algorithm name */
+                       pos = strchr(algo_digest, '\0');
+                       if (!pos)
+                       {
+                               error = "no algo field";
+                               break;
+                       }
+                       algo_len = pos - algo_digest + 1;
+
+                       if (algo_len > IMA_ALGO_LEN_MAX ||
+                               algo_len < IMA_ALGO_LEN_MIN || *(pos - 1) != ':')
+                       {
+                               error = "invalid algo field";
+                               break;
+                       }
+
+                       /* copy and store the hash algorithm name */
+                       entry->algo = malloc(algo_len);
+                       memcpy(entry->algo, algo_digest, algo_len);
+
+                       /* read the 32 bit length of the event name in host order */
+                       if (read(fd, &name_len, 4) != 4 ||
+                               eventdata_len != 4 + algo_digest_len + 4 + name_len)
+                       {
+                               error = "invalid filename field length";
+                               break;
+                       }
+
+                       /* allocate memory for the file name */
+                       entry->name = malloc(name_len);
+
+                       /* read file name */
+                       if (read(fd, entry->name, name_len) != name_len)
+                       {
+                               error = "invalid filename field";
+                               break;
+                       }
+               }
+               else
+               {
+                       /* skip SHA-1 digest of the file content */
+                       if (lseek(fd, HASH_SIZE_SHA1, SEEK_CUR) == -1)
+                       {
+                               break;
+                       }
+
+                       /* read the 32 bit length of the file name in host order */
+                       if (read(fd, &name_len, 4) != 4)
+                       {
+                               error = "invalid filename field length";
+                               break;
+                       }
+
+                       /* allocate memory for the file name */
+                       entry->name = malloc(name_len + 1);
+
+                       /* read file name */
+                       if (read(fd, entry->name, name_len) != name_len)
+                       {
+                               error = "invalid eventname field";
+                               break;
+                       }
+
+                       /* terminate the file name with a nul character */
+                       entry->name[name_len] = '\0';
+               }
+
+               this->list->insert_last(this->list, entry);
+       }
+
+       DBG1(DBG_PTS, "loading ima measurements '%s' failed: %s", file, error);
+       free_event_entry(entry);
+       close(fd);
+       destroy(this);
+
+       return NULL;
+}
diff --git a/src/libpts/pts/pts_ima_event_list.h b/src/libpts/pts/pts_ima_event_list.h
new file mode 100644 (file)
index 0000000..bf5478a
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2014 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 pts_ima_event_list pts_ima_event_list
+ * @{ @ingroup pts
+ */
+
+#ifndef PTS_IMA_EVENT_LIST_H_
+#define PTS_IMA_EVENT_LIST_H_
+
+#include <time.h>
+
+#include <library.h>
+
+typedef struct pts_ima_event_list_t pts_ima_event_list_t;
+
+#define IMA_PCR                                10
+#define IMA_ALGO_LEN_MIN       5
+#define IMA_ALGO_LEN_MAX       8
+
+
+/**
+ * Class retrieving Linux IMA file measurements
+ *
+ */
+struct pts_ima_event_list_t {
+
+       /**
+        * Get the time the file measurements were taken
+        *
+        * @return                              Measurement time
+        */
+       time_t (*get_time)(pts_ima_event_list_t *this);
+
+       /**
+        * Get the number of non-processed file measurements
+        *
+        * @return                              Number of measurements left
+        */
+       int (*get_count)(pts_ima_event_list_t *this);
+
+       /**
+        * Get the next file measurement and remove it from the list
+        *
+        * @param measurement   Measurement hash
+        * @param algo                  Algorithm used to hash files
+        " @param name                  Event name (absolute filename or boot_aggregate) 
+        * @return                              Return code
+        */
+       status_t (*get_next)(pts_ima_event_list_t *this, chunk_t *measurement,
+                                                char **algo, char **name);
+
+       /**
+        * Destroys a pts_ima_event_list_t object.
+        */
+       void (*destroy)(pts_ima_event_list_t *this);
+
+};
+
+/**
+ * Create a PTS IMA runtime file measurement object
+ *
+ * @param file                         Pathname pointing to the IMA runtme measurements
+ */
+pts_ima_event_list_t* pts_ima_event_list_create(char *file);
+
+#endif /** PTS_IMA_EVENT_LIST_H_ @}*/