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 \
#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 {
/**
* 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
bool pcr_info;
/**
- * IMA measurement time
+ * Creation time of measurement
*/
- time_t measurement_time;
+ time_t creation_time;
/**
* IMA state machine
};
-/**
- * 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
*/
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));
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);
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;
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");
}
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;
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))
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);
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,
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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_ @}*/
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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_ @}*/