transfer IMA file measurements via PA-TNC
[strongswan.git] / src / libpts / pts / components / ita / ita_comp_ima.c
index e9c6b9d..2de3caf 100644 (file)
@@ -1,6 +1,5 @@
 /*
- * Copyright (C) 2011 Andreas Steffen
- *
+ * Copyright (C) 2011-2012 Andreas Steffen
  * HSR Hochschule fuer Technik Rapperswil
  *
  * This program is free software; you can redistribute it and/or modify it
@@ -32,6 +31,7 @@
 #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_EVENT_NAME_LEN_MAX         255
 #define IMA_PCR                                                10
 #define IMA_PCR_MAX                                    16
 #define IMA_TYPE_LEN                           3
@@ -44,7 +44,7 @@ typedef enum ima_state_t ima_state_t;
 enum ima_state_t {
        IMA_STATE_INIT,
        IMA_STATE_BIOS,
-       IMA_STATE_BIOS_AGGREGATE,
+       IMA_STATE_BOOT_AGGREGATE,
        IMA_STATE_RUNTIME,
        IMA_STATE_END
 };
@@ -171,7 +171,7 @@ struct ima_entry_t {
        /**
         * absolute path of executable files or basename of dynamic libraries
         */
-       chunk_t filename;
+       char *filename;
 };
 
 /**
@@ -190,7 +190,7 @@ static void free_ima_entry(ima_entry_t *this)
 {
        free(this->measurement.ptr);
        free(this->file_measurement.ptr);
-       free(this->filename.ptr);
+       free(this->filename);
        free(this);
 }
 
@@ -310,7 +310,7 @@ static bool load_runtime_measurements(char *file, linked_list_t *list,
                entry = malloc_thing(ima_entry_t);
                entry->measurement = chunk_alloc(HASH_SIZE_SHA1);
                entry->file_measurement = chunk_alloc(HASH_SIZE_SHA1);
-               entry->filename = chunk_empty;
+               entry->filename = NULL;
 
                if (res != 4 || pcr != IMA_PCR)
                {
@@ -337,11 +337,13 @@ static bool load_runtime_measurements(char *file, linked_list_t *list,
                {
                        break;
                }
-               entry->filename = chunk_alloc(len);
-               if (read(fd, entry->filename.ptr, len) != len)
+               entry->filename = malloc(len + 1);
+               if (read(fd, entry->filename, len) != len)
                {
                        break;
                }
+               entry->filename[len] = '\0';
+
                list->insert_last(list, entry);
        }
 
@@ -355,14 +357,16 @@ static bool load_runtime_measurements(char *file, linked_list_t *list,
  * Extend measurement into PCR an create evidence
  */
 pts_comp_evidence_t* extend_pcr(pts_ita_comp_ima_t* this, u_int32_t pcr,
-                                                               size_t pcr_len, chunk_t measurement)
+                                                               chunk_t measurement)
 {
+       size_t pcr_len;
        pts_pcr_transform_t pcr_transform;
        pts_meas_algorithms_t hash_algo;
        pts_comp_evidence_t *evidence;
        chunk_t pcr_before, pcr_after;
 
        hash_algo = PTS_MEAS_ALGO_SHA1;
+       pcr_len = HASH_SIZE_SHA1;
        pcr_transform = pts_meas_algo_to_pcr_transform(hash_algo, pcr_len);
        pcr_before = chunk_clone(this->pcrs[pcr]);
        this->hasher->get_hash(this->hasher, pcr_before, NULL);
@@ -377,6 +381,35 @@ pts_comp_evidence_t* extend_pcr(pts_ita_comp_ima_t* this, u_int32_t pcr,
        return evidence;
 }
 
+/**
+ * Compute and check boot aggregate value by hashing PCR0 to PCR7
+ */
+void check_boot_aggregate(pts_ita_comp_ima_t *this, chunk_t measurement)
+{
+       u_int32_t pcr;
+       u_char pcr_buffer[HASH_SIZE_SHA1];
+       u_char boot_aggregate_name[] = "boot_aggregate";
+       u_char filename_buffer[IMA_EVENT_NAME_LEN_MAX + 1];
+       chunk_t boot_aggregate, file_name;
+
+       /* See Linux kernel header: security/integrity/ima/ima.h */
+       boot_aggregate = chunk_create(pcr_buffer, sizeof(pcr_buffer));
+       memset(filename_buffer, 0, sizeof(filename_buffer));
+       strcpy(filename_buffer, boot_aggregate_name);
+       file_name = chunk_create(filename_buffer, sizeof(filename_buffer));
+
+       for (pcr = 0; pcr < 8; pcr++)
+       {
+               this->hasher->get_hash(this->hasher, this->pcrs[pcr], NULL);
+       }
+       this->hasher->get_hash(this->hasher, chunk_empty, pcr_buffer);
+       this->hasher->get_hash(this->hasher, boot_aggregate, NULL);
+       this->hasher->get_hash(this->hasher, file_name, pcr_buffer);
+
+       DBG1(DBG_PTS, "boot aggregate value is %scorrect",
+                chunk_equals(boot_aggregate, measurement) ? "":"in");
+}
+
 METHOD(pts_component_t, get_comp_func_name, pts_comp_func_name_t*,
        pts_ita_comp_ima_t *this)
 {
@@ -396,11 +429,16 @@ METHOD(pts_component_t, get_depth, u_int32_t,
 }
 
 METHOD(pts_component_t, measure, status_t,
-       pts_ita_comp_ima_t *this, pts_t *pts, pts_comp_evidence_t **evidence)
+       pts_ita_comp_ima_t *this, pts_t *pts, pts_comp_evidence_t **evidence,
+       pts_file_meas_t **measurements)
 {
        bios_entry_t *bios_entry;
-       ima_entry_t *ima_entry;
+       ima_entry_t *ima_entry, *entry;
        status_t status;
+       enumerator_t *e;
+       pts_file_meas_t *file_meas;
+
+       *measurements = NULL;
 
        switch (this->state)
        {
@@ -420,8 +458,8 @@ METHOD(pts_component_t, measure, status_t,
                                DBG1(DBG_PTS, "could not retrieve bios measurement entry");
                                return status;
                        }
-                       *evidence = extend_pcr(this, bios_entry->pcr, pts->get_pcr_len(pts),
-                                                                  bios_entry->measurement);
+                       *evidence = extend_pcr(this, bios_entry->pcr,
+                                                                                bios_entry->measurement);
                        free(bios_entry);
        
                        /* break if still some BIOS measurements are left */
@@ -438,9 +476,9 @@ METHOD(pts_component_t, measure, status_t,
                        }
 
                        this->state = this->ima_list->get_count(this->ima_list) ?
-                                                                       IMA_STATE_BIOS_AGGREGATE : IMA_STATE_END;
+                                                                       IMA_STATE_BOOT_AGGREGATE : IMA_STATE_END;
                        break;
-               case IMA_STATE_BIOS_AGGREGATE:
+               case IMA_STATE_BOOT_AGGREGATE:
                case IMA_STATE_RUNTIME:
                        status = this->ima_list->remove_first(this->ima_list,
                                                                                                 (void**)&ima_entry);
@@ -449,24 +487,36 @@ METHOD(pts_component_t, measure, status_t,
                                DBG1(DBG_PTS, "could not retrieve ima measurement entry");
                                return status;
                        }
-                       *evidence = extend_pcr(this, IMA_PCR, pts->get_pcr_len(pts),
-                                                                  ima_entry->measurement);
-
-                       /* TODO optionally send file measurements */
-                       chunk_free(&ima_entry->file_measurement);
-                       chunk_free(&ima_entry->filename);
-                       free(ima_entry);
+                       *evidence = extend_pcr(this, IMA_PCR, ima_entry->measurement);
 
-                       if (this->state == IMA_STATE_BIOS_AGGREGATE)
+                       if (this->state == IMA_STATE_BOOT_AGGREGATE)
                        {
-                               /* TODO check BIOS aggregate value */
+                               check_boot_aggregate(this, ima_entry->measurement);
+
+                               if (this->ima_list->get_count(this->ima_list))
+                               {
+                                       /* extract file measurements */
+                                       file_meas = pts_file_meas_create(0);
+
+                                       e = this->ima_list->create_enumerator(this->ima_list);
+                                       while (e->enumerate(e, &entry))
+                                       {
+                                               file_meas->add(file_meas, entry->filename,
+                                                                                                 entry->file_measurement);
+                                       }
+                                       e->destroy(e);
+                                       *measurements = file_meas;
+                               }
                        }
+
+                       free(ima_entry->file_measurement.ptr);
+                       free(ima_entry->filename);
+                       free(ima_entry);
                        this->state = this->ima_list->get_count(this->ima_list) ?
                                                                        IMA_STATE_RUNTIME : IMA_STATE_END;
                        break;
                case IMA_STATE_END:
-                       /* shouldn't happen */
-                       return FAILED;
+                       break;
        }
        
        return (this->state == IMA_STATE_END) ? SUCCESS : NEED_MORE;
@@ -487,66 +537,81 @@ METHOD(pts_component_t, verify, status_t,
        measurement = evidence->get_measurement(evidence, &extended_pcr,
                                                                &algo, &transform, &measurement_time);
 
-       if (!this->keyid.ptr)
+       switch (this->state)
        {
-               if (!pts->get_aik_keyid(pts, &this->keyid))
-               {
-                       return FAILED;
-               }
-               this->keyid = chunk_clone(this->keyid);
-
-               if (!this->pts_db)
-               {
-                       DBG1(DBG_PTS, "pts database not available");
-                       return FAILED;
-               }
-               status = this->pts_db->get_comp_measurement_count(this->pts_db,
-                                                               this->name, this->keyid, algo,
-                                                               &this->cid, &this->kid, &this->count);
-               if (status != SUCCESS)
-               {
-                       return status;
-               }
-               vid = this->name->get_vendor_id(this->name);
-               name = this->name->get_name(this->name);
-               names = pts_components->get_comp_func_names(pts_components, vid);
-
-               if (this->count)
-               {
-                       DBG1(DBG_PTS, "checking %d %N '%N' functional component evidence "
-                                "measurements", this->count, pen_names, vid, names, name);
-               }
-               else
-               {
-                       DBG1(DBG_PTS, "registering %N '%N' functional component evidence "
-                                "measurements", pen_names, vid, names, name);
-                       this->is_registering = TRUE;
-               }
-       }
+               case IMA_STATE_INIT:
+                       if (!pts->get_aik_keyid(pts, &this->keyid))
+                       {
+                               return FAILED;
+                       }
+                       this->keyid = chunk_clone(this->keyid);
 
-       if (extended_pcr != IMA_PCR)
-       {
-               if (this->is_registering)
-               {
-                       status = this->pts_db->insert_comp_measurement(this->pts_db,
-                                                                       measurement, this->cid, this->kid,
-                                                                       ++this->seq_no, extended_pcr, algo);
-                       if (status != SUCCESS)
+                       if (!this->pts_db)
                        {
-                               return status;
+                               DBG1(DBG_PTS, "pts database not available");
+                               return FAILED;
                        }
-                       this->count = this->seq_no + 1;
-               }
-               else
-               {
-                       status = this->pts_db->check_comp_measurement(this->pts_db,
-                                                                       measurement, this->cid, this->kid,
-                                                                       ++this->seq_no, extended_pcr, algo);
+                       status = this->pts_db->get_comp_measurement_count(this->pts_db,
+                                                                       this->name, this->keyid, algo,
+                                                                       &this->cid, &this->kid, &this->count);
                        if (status != SUCCESS)
                        {
                                return status;
                        }
-               }
+                       vid = this->name->get_vendor_id(this->name);
+                       name = this->name->get_name(this->name);
+                       names = pts_components->get_comp_func_names(pts_components, vid);
+
+                       if (this->count)
+                       {
+                               DBG1(DBG_PTS, "checking %d %N '%N' functional component "
+                                                         "evidence measurements", this->count, pen_names,
+                                                          vid, names, name);
+                       }
+                       else
+                       {
+                               DBG1(DBG_PTS, "registering %N '%N' functional component "
+                                                         "evidence measurements", pen_names, vid, names,
+                                                          name);
+                               this->is_registering = TRUE;
+                       }
+                       this->state = IMA_STATE_BIOS;
+                       /* fall through to next state */
+               case IMA_STATE_BIOS:
+                       if (extended_pcr != IMA_PCR)
+                       {
+                               if (this->is_registering)
+                               {
+                                       status = this->pts_db->insert_comp_measurement(this->pts_db,
+                                                                                       measurement, this->cid, this->kid,
+                                                                                       ++this->seq_no, extended_pcr, algo);
+                                       if (status != SUCCESS)
+                                       {
+                                               return status;
+                                       }
+                                       this->count = this->seq_no + 1;
+                               }
+                               else
+                               {
+                                       status = this->pts_db->check_comp_measurement(this->pts_db,
+                                                                                       measurement, this->cid, this->kid,
+                                                                                       ++this->seq_no, extended_pcr, algo);
+                                       if (status != SUCCESS)
+                                       {
+                                               return status;
+                                       }
+                               }
+                               break;
+                       }
+                       this->state = IMA_STATE_BOOT_AGGREGATE;
+                       /* fall through to next state */
+               case IMA_STATE_BOOT_AGGREGATE:
+                       this->state = IMA_STATE_RUNTIME;
+                       break;
+               case IMA_STATE_RUNTIME:
+                       break;
+               case IMA_STATE_END:
+                       break;
        }
 
        has_pcr_info = evidence->get_pcr_info(evidence, &pcr_before, &pcr_after);