From: Andreas Steffen Date: Thu, 1 May 2014 20:13:06 +0000 (+0200) Subject: Moved BIOS and IMA measurement lists into classes of their own X-Git-Tag: 5.2.0dr2~5 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=48064815ea697da9881553d71a858d05fc58aa28;p=thirdparty%2Fstrongswan.git Moved BIOS and IMA measurement lists into classes of their own --- diff --git a/src/libpts/Makefile.am b/src/libpts/Makefile.am index 48bb46c7a7..6bd3a58fcf 100644 --- a/src/libpts/Makefile.am +++ b/src/libpts/Makefile.am @@ -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 \ diff --git a/src/libpts/pts/components/ita/ita_comp_ima.c b/src/libpts/pts/components/ita/ita_comp_ima.c index b5a91cafaa..7a7f6b9ad1 100644 --- a/src/libpts/pts/components/ita/ita_comp_ima.c +++ b/src/libpts/pts/components/ita/ita_comp_ima.c @@ -18,33 +18,20 @@ #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 #include #include -#include -#include -#include -#include -#include - #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 @@ -173,333 +160,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 */ @@ -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 index 0000000000..18c41f2272 --- /dev/null +++ b/src/libpts/pts/pts_ima_bios_list.c @@ -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 . + * + * 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 + +#include +#include +#include +#include +#include + +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 index 0000000000..ad162e15a8 --- /dev/null +++ b/src/libpts/pts/pts_ima_bios_list.h @@ -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 . + * + * 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 + +#include + +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 index 0000000000..a237451ca5 --- /dev/null +++ b/src/libpts/pts/pts_ima_event_list.c @@ -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 . + * + * 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 +#include + +#include +#include +#include +#include +#include + +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 index 0000000000..bf5478a512 --- /dev/null +++ b/src/libpts/pts/pts_ima_event_list.h @@ -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 . + * + * 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 + +#include + +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_ @}*/