Moved BIOS and IMA measurement lists into classes of their own
This commit is contained in:
@@ -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 \
|
||||
|
||||
@@ -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 <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
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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_ @}*/
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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_ @}*/
|
||||
Reference in New Issue
Block a user