#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_FILENAME_LEN_MAX 255
typedef struct pts_ita_comp_ima_t pts_ita_comp_ima_t;
typedef enum ima_state_t ima_state_t;
*/
bool pcr_info;
+ /**
+ * Whether to pad PCR measurements if matching hash is not available
+ */
+ bool pcr_padding;
+
/**
* Creation time of measurement
*/
*/
static pts_comp_evidence_t* extend_pcr(pts_ita_comp_ima_t* this,
uint8_t qualifier, pts_pcr_t *pcrs,
- uint32_t pcr, chunk_t measurement)
+ uint32_t pcr, chunk_t measurement,
+ pts_pcr_transform_t pcr_transform)
{
- pts_pcr_transform_t pcr_transform;
pts_meas_algorithms_t pcr_algo;
pts_comp_func_name_t *name;
pts_comp_evidence_t *evidence;
chunk_t pcr_before = chunk_empty, pcr_after = chunk_empty;
pcr_algo = pcrs->get_pcr_algo(pcrs);
- pcr_transform = PTS_PCR_TRANSFORM_MATCH;
if (this->pcr_info)
{
}
/**
- * Generate an IMA or IMA-NG hash from an event digest and event name
- *
- * @param digest event digest
- * @param ima_algo hash algorithm string ("sha1:", "sha256:", etc.)
- * @param ima_name event name
- * @param little_endian endianness of client platform
- * @param algo hash algorithm used by TPM
- * @param hash_buf hash value to be compared with TPM measurement
+ * Compute and check boot aggregate value by hashing PCR0 to PCR7
*/
-static bool ima_hash(chunk_t digest, char *ima_algo, char *ima_name,
- bool little_endian, pts_meas_algorithms_t algo,
- char *hash_buf)
+static bool check_boot_aggregate(pts_pcr_t *pcrs, char *algo, bool pcr_padding,
+ chunk_t boot_aggregate, chunk_t measurement)
+
{
+ chunk_t ba_measurement;
+ uint8_t meas_buffer[HASH_SIZE_SHA512];
+ size_t hash_size;
+ pts_meas_algorithms_t pcr_algo;
hash_algorithm_t hash_alg;
hasher_t *hasher;
- bool success;
-
- hash_alg = pts_meas_algo_to_hash(algo);
- hasher = lib->crypto->create_hasher(lib->crypto, hash_alg);
- if (!hasher)
- {
- DBG1(DBG_PTS, "%N hasher could not be created",
- hash_algorithm_short_names, hash_alg);
- return FALSE;
- }
+ uint32_t i, pcr_max;
+ bool success, pcr_ok = TRUE;
- if (ima_algo)
- {
- uint32_t d_len, n_len;
- chunk_t algo_name, event_name, digest_len, name_len;
-
- /* IMA-NG hash */
- algo_name = chunk_create(ima_algo, strlen(ima_algo) + 1);
- event_name = chunk_create(ima_name, strlen(ima_name) + 1);
-
- d_len = algo_name.len + digest.len;
- digest_len = chunk_create((uint8_t*)&d_len, sizeof(d_len));
- /* TODO handle endianness of both client and server platforms */
-
- n_len = event_name.len;
- name_len = chunk_create((uint8_t*)&n_len, sizeof(n_len));
- /* TODO handle endianness of both client and server platforms */
-
- success = hasher->get_hash(hasher, digest_len, NULL) &&
- hasher->get_hash(hasher, algo_name, NULL) &&
- hasher->get_hash(hasher, digest, NULL) &&
- hasher->get_hash(hasher, name_len, NULL) &&
- hasher->get_hash(hasher, event_name, hash_buf);
- }
- else
+ /* determine PCR hash algorithm and the need for PCR padding */
+ pcr_algo = pcrs->get_pcr_algo(pcrs);
+ if (pcr_algo == PTS_MEAS_ALGO_SHA1)
{
- u_char filename_buffer[IMA_FILENAME_LEN_MAX + 1];
- chunk_t file_name;
-
- /* IMA legacy hash */
- memset(filename_buffer, 0, sizeof(filename_buffer));
- strncpy(filename_buffer, ima_name, IMA_FILENAME_LEN_MAX);
- file_name = chunk_create (filename_buffer, sizeof(filename_buffer));
-
- success = hasher->get_hash(hasher, digest, NULL) &&
- hasher->get_hash(hasher, file_name, hash_buf);
+ pcr_padding = FALSE;
}
- hasher->destroy(hasher);
-
- return success;
-}
-/**
- * Compute and check boot aggregate value by hashing PCR0 to PCR7
- */
-static bool check_boot_aggregate(pts_pcr_t *pcrs, chunk_t measurement,
- char *algo)
-{
- u_char pcr_buffer[HASH_SIZE_SHA1];
- chunk_t boot_aggregate;
- hasher_t *hasher;
- uint32_t i;
- bool success, pcr_ok = TRUE;
- hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1);
+ /* create hasher for boot aggregate computation */
+ hash_alg = pts_meas_algo_to_hash(pcr_algo);
+ hasher = lib->crypto->create_hasher(lib->crypto, hash_alg);
if (!hasher)
{
DBG1(DBG_PTS, "%N hasher could not be created",
- hash_algorithm_short_names, HASH_SHA1);
+ hash_algorithm_short_names, hash_alg);
return FALSE;
}
- for (i = 0; i < 8 && pcr_ok; i++)
+ hash_size = hasher->get_hash_size(hasher);
+
+ /* Include PCR8 and PCR9 in boot aggregate with unpadded non-SHA1 hashes */
+ pcr_max = (pcr_algo == PTS_MEAS_ALGO_SHA1 || pcr_padding) ? 7 : 9;
+
+ /* the boot aggregate hash is computed over PCR0 .. PCR7/PCR9 */
+ for (i = 0; i <= pcr_max && pcr_ok; i++)
{
pcr_ok = hasher->get_hash(hasher, pcrs->get(pcrs, i), NULL);
}
if (pcr_ok)
{
- pcr_ok = hasher->get_hash(hasher, chunk_empty, pcr_buffer);
+ pcr_ok = hasher->get_hash(hasher, chunk_empty, boot_aggregate.ptr);
}
hasher->destroy(hasher);
+
if (pcr_ok)
{
- boot_aggregate = chunk_create(pcr_buffer, sizeof(pcr_buffer));
-
- /* TODO handle endianness of client platform */
- pcr_ok = ima_hash(boot_aggregate, algo, "boot_aggregate",
- TRUE, PTS_MEAS_ALGO_SHA1, pcr_buffer);
+ ba_measurement = chunk_create(meas_buffer, hash_size);
+ if (pcr_padding)
+ {
+ memset(meas_buffer, 0x00, hash_size);
+ pcr_algo = PTS_MEAS_ALGO_SHA1;
+ }
+ pcr_ok = pts_ima_event_hash(boot_aggregate, algo, "boot_aggregate",
+ pcr_algo, meas_buffer);
}
if (pcr_ok)
{
- success = chunk_equals_const(boot_aggregate, measurement);
- DBG1(DBG_PTS, "boot aggregate value is %scorrect",
- success ? "":"in");
+ success = chunk_equals_const(ba_measurement, measurement);
+ DBG1(DBG_PTS, "boot aggregate computed over PCR0..PCR%d is %scorrect",
+ pcr_max, success ? "":"in");
return success;
}
else
pts_comp_evidence_t **evidence)
{
pts_pcr_t *pcrs;
+ pts_meas_algorithms_t pcr_algo;
pts_comp_evidence_t *evid = NULL;
- size_t algo_len, name_len;
- chunk_t measurement;
+ size_t algo_len, name_len, pcr_size;
+ chunk_t measurement, boot_aggregate;
+ uint8_t pcr_buffer[HASH_SIZE_SHA512];
char *uri, *algo, *name;
uint32_t pcr;
status_t status;
{
return FAILED;
}
+ pcr_algo = pcrs->get_pcr_algo(pcrs);
+ pcr_size = pts_meas_algo_hash_size(pcr_algo);
if (qualifier == (PTS_ITA_QUALIFIER_FLAG_KERNEL |
PTS_ITA_QUALIFIER_TYPE_TRUSTED))
{
case IMA_STATE_INIT:
this->bios_list = pts_ima_bios_list_create(pts->get_tpm(pts),
- IMA_BIOS_MEASUREMENTS,
- pcrs->get_pcr_algo(pcrs));
+ IMA_BIOS_MEASUREMENTS, pcr_algo);
if (!this->bios_list)
{
return FAILED;
DBG1(DBG_PTS, "could not retrieve bios measurement entry");
return status;
}
- evid = extend_pcr(this, qualifier, pcrs, pcr, measurement);
+ evid = extend_pcr(this, qualifier, pcrs, pcr, measurement,
+ PTS_PCR_TRANSFORM_MATCH);
this->state = this->bios_list->get_count(this->bios_list) ?
IMA_STATE_BIOS : IMA_STATE_INIT;
switch (this->state)
{
case IMA_STATE_INIT:
+
+ /* disable padding for SHA1 legacy hash */
+ if (pcr_algo == PTS_MEAS_ALGO_SHA1)
+ {
+ this->pcr_padding = FALSE;
+ }
+
this->ima_list = pts_ima_event_list_create(
- IMA_RUNTIME_MEASUREMENTS);
+ IMA_RUNTIME_MEASUREMENTS,
+ pcr_algo, this->pcr_padding);
if (!this->ima_list)
{
return FAILED;
}
if (this->state == IMA_STATE_BOOT_AGGREGATE && this->bios_count)
{
- if (!check_boot_aggregate(pcrs, measurement, algo))
+ boot_aggregate = chunk_create(pcr_buffer, pcr_size);
+ if (!check_boot_aggregate(pcrs, algo, this->pcr_padding,
+ boot_aggregate, measurement))
{
return FAILED;
}
}
- evid = extend_pcr(this, qualifier, pcrs, IMA_PCR,
- measurement);
+
+ evid = extend_pcr(this, qualifier, pcrs, IMA_PCR, measurement,
+ this->pcr_padding ? PTS_PCR_TRANSFORM_SHORT :
+ PTS_PCR_TRANSFORM_MATCH);
if (evid)
{
if (algo)
{
bool has_pcr_info;
uint32_t pcr;
- pts_meas_algorithms_t algo;
+ size_t pcr_size;
+ pts_meas_algorithms_t algo, pcr_algo;
pts_pcr_transform_t transform;
pts_pcr_t *pcrs;
time_t creation_time;
status_t status = NOT_FOUND;
this->aik_id = pts->get_aik_id(pts);
+
+
pcrs = pts->get_pcrs(pts);
if (!pcrs)
{
return FAILED;
}
+ pcr_algo = pcrs->get_pcr_algo(pcrs);
+ pcr_size = pts_meas_algo_hash_size(pcr_algo);
+
measurement = evidence->get_measurement(evidence, &pcr, &algo, &transform,
&creation_time);
+ if (algo != pcr_algo)
+ {
+ DBG1(DBG_PTS, "received %N measurement hash but PCR bank is %N",
+ pts_meas_algorithm_names, algo, pts_meas_algorithm_names, algo);
+ return FAILED;
+ }
+ this->pcr_padding = (transform == PTS_PCR_TRANSFORM_SHORT);
if (qualifier == (PTS_ITA_QUALIFIER_FLAG_KERNEL |
PTS_ITA_QUALIFIER_TYPE_TRUSTED))
int ima_count;
char *ima_algo, *ima_name;
char algo_buf[IMA_ALGO_LEN_MAX];
+ uint8_t pcr_buffer[HASH_SIZE_SHA512];
+ chunk_t boot_aggregate;
pts_meas_algorithms_t hash_algo;
hash_algo = parse_validation_uri(evidence, &ima_name, &ima_algo,
"but is '%s'", ima_name);
return FAILED;
}
- if (hash_algo != PTS_MEAS_ALGO_SHA1)
+ if (hash_algo != pcr_algo)
{
DBG1(DBG_PTS, "ima: boot_aggregate algorithm must be %N "
"but is %N",
- pts_meas_algorithm_names, PTS_MEAS_ALGO_SHA1,
+ pts_meas_algorithm_names, pcr_algo,
pts_meas_algorithm_names, hash_algo);
return FAILED;
}
- if (!check_boot_aggregate(pcrs, measurement, ima_algo))
+ boot_aggregate = chunk_create(pcr_buffer, pcr_size);
+ if (!check_boot_aggregate(pcrs, ima_algo, this->pcr_padding,
+ boot_aggregate, measurement))
{
return FAILED;
}
DBG1(DBG_PTS, "checking boot aggregate evidence "
"measurement");
status = this->pts_db->check_comp_measurement(this->pts_db,
- measurement, this->ima_cid,
- this->aik_id, 1, pcr, algo);
+ boot_aggregate, this->ima_cid,
+ this->aik_id, 1, pcr, algo);
}
else
{
"measurement");
this->is_ima_registering = TRUE;
status = this->pts_db->insert_comp_measurement(this->pts_db,
- measurement, this->ima_cid,
- this->aik_id, 1, pcr, algo);
+ boot_aggregate, this->ima_cid,
+ this->aik_id, 1, pcr, algo);
}
this->state = IMA_STATE_RUNTIME;
break;
case IMA_STATE_RUNTIME:
{
- uint8_t hash_buf[HASH_SIZE_SHA512];
- uint8_t digest_buf[HASH_SIZE_SHA512], *hex_digest_buf;
chunk_t hex_digest, digest, hash;
+ uint8_t digest_buf[HASH_SIZE_SHA512], *hex_digest_buf;
+ uint8_t hash_buf[HASH_SIZE_SHA512];
+ size_t hash_size;
+ pts_meas_algorithms_t meas_algo;
enumerator_t *e;
this->count++;
if (evidence->get_validation(evidence, NULL) !=
- PTS_COMP_EVID_VALIDATION_PASSED)
+ PTS_COMP_EVID_VALIDATION_PASSED)
{
DBG1(DBG_PTS, "evidence validation failed");
this->count_failed++;
return FAILED;
}
- hash = chunk_create(hash_buf, pts_meas_algo_hash_size(algo));
+ hash_size = pts_meas_algo_hash_size(algo);
+ hash = chunk_create(hash_buf, hash_size);
+
+ if (this->pcr_padding)
+ {
+ memset(hash_buf, 0x00, hash_size);
+ meas_algo = PTS_MEAS_ALGO_SHA1;
+ }
+ else
+ {
+ meas_algo = algo;
+ }
e = this->pts_db->create_file_meas_enumerator(this->pts_db,
pts->get_platform_id(pts),
{
hex_digest = chunk_from_str(hex_digest_buf);
digest = chunk_from_hex(hex_digest, digest_buf);
- if (!ima_hash(digest, ima_algo, ima_name,
- FALSE, algo, hash_buf))
+
+ if (!pts_ima_event_hash(digest, ima_algo, ima_name,
+ meas_algo, hash_buf))
{
status = FAILED;
break;
.pts_db = pts_db,
.pcr_info = lib->settings->get_bool(lib->settings,
"%s.plugins.imc-attestation.pcr_info", FALSE, lib->ns),
+ .pcr_padding = lib->settings->get_bool(lib->settings,
+ "%s.plugins.imc-attestation.pcr_padding", FALSE, lib->ns),
.ref = 1,
);
#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
+#define IMA_FILENAME_LEN_MAX 255
+
+
/**
* Private data of a pts_ima_event_list_t object.
struct event_entry_t {
/**
- * SHA1 measurement hash
+ * Special IMA measurement hash
*/
chunk_t measurement;
/**
* See header
*/
-pts_ima_event_list_t* pts_ima_event_list_create(char *file)
+pts_ima_event_list_t* pts_ima_event_list_create(char *file,
+ pts_meas_algorithms_t pcr_algo, bool pcr_padding)
{
private_pts_ima_event_list_t *this;
event_entry_t *entry;
+ chunk_t digest;
uint32_t pcr, type_len, name_len, eventdata_len, algo_digest_len, algo_len;
+ size_t hash_size;
char type[IMA_TYPE_LEN_MAX];
char algo_digest[IMA_ALGO_DIGEST_LEN_MAX];
char *pos, *error = "";
.list = linked_list_create(),
);
+ hash_size = pts_meas_algo_hash_size(pcr_algo);
+
while (TRUE)
{
/* read 32 bit PCR number in host order */
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->measurement = chunk_alloc(hash_size);
entry->algo = NULL;
entry->name = NULL;
break;
}
- /* read 20 byte SHA-1 measurement digest */
+ if (pcr_padding)
+ {
+ memset(entry->measurement.ptr, 0x00, hash_size);
+ }
+
+ /* read 20 byte SHA-1 IMA measurement digest */
if (read(fd, entry->measurement.ptr, HASH_SIZE_SHA1) != HASH_SIZE_SHA1)
{
error = "invalid SHA-1 digest field";
entry->algo = malloc(algo_len);
memcpy(entry->algo, algo_digest, algo_len);
+ /* extract the digest */
+ digest = chunk_create(pos + 1, algo_digest_len - 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";
break;
}
+
+ /* re-compute IMA measurement digest for non-SHA1 hash algorithms */
+ if (pcr_algo != PTS_MEAS_ALGO_SHA1 && !pcr_padding)
+ {
+ if (!pts_ima_event_hash(digest, entry->algo, entry->name,
+ pcr_algo, entry->measurement.ptr))
+ {
+ break;
+ }
+
+ }
}
else
{
return NULL;
}
+
+/**
+ * See header
+ */
+bool pts_ima_event_hash(chunk_t digest, char *ima_algo, char *ima_name,
+ pts_meas_algorithms_t pcr_algo, char *hash_buf)
+{
+ hash_algorithm_t hash_alg;
+ hasher_t *hasher;
+ bool success;
+
+ hash_alg = pts_meas_algo_to_hash(pcr_algo);
+ hasher = lib->crypto->create_hasher(lib->crypto, hash_alg);
+ if (!hasher)
+ {
+ DBG1(DBG_PTS, "%N hasher could not be created",
+ hash_algorithm_short_names, hash_alg);
+ return FALSE;
+ }
+
+ if (ima_algo)
+ {
+ uint32_t ad_len, n_len;
+ chunk_t algo_name, event_name, algo_digest_len, name_len;
+
+ /* IMA-NG hash */
+ algo_name = chunk_create(ima_algo, strlen(ima_algo) + 1);
+ event_name = chunk_create(ima_name, strlen(ima_name) + 1);
+
+ ad_len = htole32(algo_name.len + digest.len);
+ algo_digest_len = chunk_create((uint8_t*)&ad_len, sizeof(ad_len));
+
+ n_len = htole32(event_name.len);
+ name_len = chunk_create((uint8_t*)&n_len, sizeof(n_len));
+
+ success = hasher->get_hash(hasher, algo_digest_len, NULL) &&
+ hasher->get_hash(hasher, algo_name, NULL) &&
+ hasher->get_hash(hasher, digest, NULL) &&
+ hasher->get_hash(hasher, name_len, NULL) &&
+ hasher->get_hash(hasher, event_name, hash_buf);
+ }
+ else
+ {
+ u_char filename_buffer[IMA_FILENAME_LEN_MAX + 1];
+ chunk_t file_name;
+
+ /* IMA legacy hash */
+ memset(filename_buffer, 0, sizeof(filename_buffer));
+ strncpy(filename_buffer, ima_name, IMA_FILENAME_LEN_MAX);
+ file_name = chunk_create (filename_buffer, sizeof(filename_buffer));
+
+ success = hasher->get_hash(hasher, digest, NULL) &&
+ hasher->get_hash(hasher, file_name, hash_buf);
+ }
+ hasher->destroy(hasher);
+
+ return success;
+}