Moved BIOS and IMA measurement lists into classes of their own

This commit is contained in:
Andreas Steffen
2014-05-02 07:30:17 +02:00
parent 9b9d522303
commit 48064815ea
6 changed files with 717 additions and 381 deletions
+2
View File
@@ -31,6 +31,8 @@ libpts_la_SOURCES = \
pts/pts_file_meas.h pts/pts_file_meas.c \
pts/pts_file_meta.h pts/pts_file_meta.c \
pts/pts_file_type.h pts/pts_file_type.c \
pts/pts_ima_bios_list.h pts/pts_ima_bios_list.c \
pts/pts_ima_event_list.h pts/pts_ima_event_list.c \
pts/pts_meas_algo.h pts/pts_meas_algo.c \
pts/components/pts_component.h \
pts/components/pts_component_manager.h pts/components/pts_component_manager.c \
+38 -381
View File
@@ -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,
+193
View File
@@ -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;
}
+74
View File
@@ -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_ @}*/
+330
View File
@@ -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;
}
+80
View File
@@ -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_ @}*/