diff --git a/src/libimcv/plugins/imv_attestation/imv_attestation.c b/src/libimcv/plugins/imv_attestation/imv_attestation.c index d32006fec..12ce27469 100644 --- a/src/libimcv/plugins/imv_attestation/imv_attestation.c +++ b/src/libimcv/plugins/imv_attestation/imv_attestation.c @@ -43,7 +43,6 @@ #include #include -#include #include /* IMV definitions */ @@ -81,11 +80,6 @@ static pts_creds_t *pts_creds; */ static credential_manager_t *pts_credmgr; -/** - * List of id's for the files that are requested for measurement - */ -static linked_list_t *requested_files; - /** * see section 3.7.1 of TCG TNC IF-IMV Specification 1.2 */ @@ -282,7 +276,6 @@ static TNC_Result send_message(TNC_ConnectionID connection_id) break; } - requested_files = linked_list_create(); while (enumerator->enumerate(enumerator, &id, &type, &pathname)) { is_directory = (type != 0); @@ -292,7 +285,8 @@ static TNC_Result send_message(TNC_ConnectionID connection_id) delimiter, pathname); attr->set_noskip_flag(attr, TRUE); msg->add_attribute(msg, attr); - requested_files->insert_last(requested_files, (void*)id); + + attestation_state->add_requested_file(attestation_state, id, type); } enumerator->destroy(enumerator); @@ -319,6 +313,15 @@ static TNC_Result send_message(TNC_ConnectionID connection_id) return result; } +/** + * String matching function with boolean return value + * Used to remove an item from linked list of strings + */ +static bool string_cmp(char *a, char *b) +{ + return (strcmp(a,b) == 0) ? TRUE : FALSE; +} + /** * see section 3.7.3 of TCG TNC IF-IMV Specification 1.2 */ @@ -518,15 +521,15 @@ TNC_Result TNC_IMV_ReceiveMessage(TNC_IMVID imv_id, DBG1(DBG_IMV, "measurement request %d returned %d file%s:", request_id, file_count, (file_count == 1) ? "":"s"); - if (!pts_db->is_directory(pts_db, request_id, &is_directory)) + if (!attestation_state->is_request_dir(attestation_state, request_id, &is_directory)) { - DBG1(DBG_IMV, "file entry with request id:%d not found", request_id); + DBG1(DBG_IMV, "received measurement with id: %d was not requested", request_id); + fatal_error = TRUE; break; } - if (!is_directory) { - requested_files->remove(requested_files, (void*)request_id, NULL); + attestation_state->remove_requested_file(attestation_state, request_id); } else { @@ -536,61 +539,46 @@ TNC_Result TNC_IMV_ReceiveMessage(TNC_IMVID imv_id, e = pts_db->create_files_in_dir_enumerator(pts_db, request_id); while (e->enumerate(e, &file)) { - files_in_dir_with_meas->insert_last(files_in_dir_with_meas, file); - DBG3(DBG_IMV, "expecting measurement for: %s with request_id: %d", file, request_id); + char *file_copy = (char *)malloc(strlen(file) * sizeof(char)); + strcpy(file_copy, file); + + files_in_dir_with_meas->insert_last(files_in_dir_with_meas, file_copy); + DBG3(DBG_IMV, "expecting measurement for: %s with request_id: %d", file_copy, request_id); } + e->destroy(e); } e_meas = measurements->create_enumerator(measurements); while (e_meas->enumerate(e_meas, &filename, &measurement)) { - enumerator_t *e; - chunk_t db_measurement; - - e = (is_directory) ? pts_db->create_dir_meas_enumerator(pts_db, - platform_info, request_id, filename, algo) : - pts_db->create_file_meas_enumerator(pts_db, - platform_info, request_id, algo); - if (!e) + bool hash_matched; + + hash_matched = pts_db->check_measurement(pts_db, + measurement, platform_info, + request_id, filename, algo, is_directory); + if (!hash_matched) { - DBG1(DBG_IMV, " database enumerator failed"); - continue; - } - if (!e->enumerate(e, &db_measurement)) - { - DBG2(DBG_IMV, " measurement for '%s' not found" - " in database", filename); - e->destroy(e); - continue; - } - if (chunk_equals(db_measurement, measurement)) - { - DBG2(DBG_IMV, " %#B for '%s' is ok", - &measurement, filename); - } - else - { - DBG1(DBG_IMV, " %#B for '%s' does not match %#B", - &measurement, filename, &db_measurement); measurement_error = TRUE; } - - if (is_directory) - { + + if (is_directory && files_in_dir_with_meas->remove(files_in_dir_with_meas, - filename, (bool (*)(void*,void*))strcmp); + filename, (bool (*)(void*,void*))string_cmp)) + { + DBG3(DBG_IMV, "Removed %s from expected files list", filename); } - e->destroy(e); } if (is_directory && !files_in_dir_with_meas->get_count(files_in_dir_with_meas)) { - requested_files->remove(requested_files, (void*)request_id, NULL); + attestation_state->remove_requested_file(attestation_state, request_id); + DBG3(DBG_IMV, "Received all expected files measured for request: %d", request_id); } - files_in_dir_with_meas->destroy(files_in_dir_with_meas); + files_in_dir_with_meas->destroy_function(files_in_dir_with_meas, free); e_meas->destroy(e_meas); + break; } @@ -630,7 +618,6 @@ TNC_Result TNC_IMV_ReceiveMessage(TNC_IMVID imv_id, pa_tnc_msg->destroy(pa_tnc_msg); - if (fatal_error) { state->set_recommendation(state, @@ -642,19 +629,19 @@ TNC_Result TNC_IMV_ReceiveMessage(TNC_IMVID imv_id, if (attestation_state->get_handshake_state(attestation_state) & IMV_ATTESTATION_STATE_END) - { - if (measurement_error || requested_files->get_count(requested_files)) + { + if (measurement_error || attestation_state->get_requests_count(attestation_state)) { enumerator_t *e; int request; - e = requested_files->create_enumerator(requested_files); + e = attestation_state->create_requests_enumerator(attestation_state); while (e->enumerate(e, &request)) { DBG1(DBG_IMV, "measurement/s not received for requests: %d", request); } - e->destroy(e); + state->set_recommendation(state, TNC_IMV_ACTION_RECOMMENDATION_ISOLATE, TNC_IMV_EVALUATION_RESULT_NONCOMPLIANT_MAJOR); @@ -733,7 +720,6 @@ TNC_Result TNC_IMV_Terminate(TNC_IMVID imv_id) } DESTROY_IF(pts_db); pts_credmgr->destroy(pts_credmgr); - requested_files->destroy(requested_files); imv_attestation->destroy(imv_attestation); imv_attestation = NULL; diff --git a/src/libimcv/plugins/imv_attestation/imv_attestation_state.c b/src/libimcv/plugins/imv_attestation/imv_attestation_state.c index e3165d0b6..00f85429f 100644 --- a/src/libimcv/plugins/imv_attestation/imv_attestation_state.c +++ b/src/libimcv/plugins/imv_attestation/imv_attestation_state.c @@ -60,6 +60,11 @@ struct private_imv_attestation_state_t { */ pts_t *pts; + /** + * Requested files and directories + */ + linked_list_t *requested_files; + }; typedef struct entry_t entry_t; @@ -157,6 +162,7 @@ METHOD(imv_state_t, destroy, void, private_imv_attestation_state_t *this) { this->pts->destroy(this->pts); + this->requested_files->destroy_function(this->requested_files, free); free(this); } @@ -178,6 +184,73 @@ METHOD(imv_attestation_state_t, get_pts, pts_t*, return this->pts; } +METHOD(imv_attestation_state_t, create_requests_enumerator, enumerator_t*, + private_imv_attestation_state_t *this) +{ + enumerator_t *e; + e = this->requested_files->create_enumerator(this->requested_files); + return e; +} + +METHOD(imv_attestation_state_t, get_requests_count, int, + private_imv_attestation_state_t *this) +{ + return this->requested_files->get_count(this->requested_files); +} + + +METHOD(imv_attestation_state_t, add_requested_file, void, + private_imv_attestation_state_t *this, int request_id, int is_dir) +{ + file_request_t *entry; + + entry = malloc_thing(file_request_t); + entry->request_id = request_id; + entry->is_dir = is_dir; + this->requested_files->insert_last(this->requested_files, entry); +} + +/** + * Comparison function to match an file request entry with its request_id + */ +static bool request_match(file_request_t *current_list_item, int request_id) +{ + return (current_list_item->request_id == request_id); +} + +METHOD(imv_attestation_state_t, remove_requested_file, bool, + private_imv_attestation_state_t *this, int request_id) +{ + file_request_t *entry; + + if (this->requested_files->find_first(this->requested_files, + (linked_list_match_t)request_match, (void**)&entry, request_id) != SUCCESS) + { + DBG1(DBG_IMV, "request entry with id: %d not found", request_id); + return FALSE; + } + + this->requested_files->remove(this->requested_files, entry, NULL); + return TRUE; +} + +METHOD(imv_attestation_state_t, is_request_dir, bool, + private_imv_attestation_state_t *this, int request_id, bool *is_dir) +{ + file_request_t *entry; + + if (this->requested_files->find_first(this->requested_files, + (linked_list_match_t)request_match, (void**)&entry, request_id) != SUCCESS) + { + DBG1(DBG_IMV, "request entry with id: %d not found", request_id); + return FALSE; + } + + *is_dir = (entry->is_dir); + return TRUE; +} + + /** * Described in header. */ @@ -199,6 +272,11 @@ imv_state_t *imv_attestation_state_create(TNC_ConnectionID connection_id) .get_handshake_state = _get_handshake_state, .set_handshake_state = _set_handshake_state, .get_pts = _get_pts, + .create_requests_enumerator = _create_requests_enumerator, + .get_requests_count = _get_requests_count, + .add_requested_file = _add_requested_file, + .remove_requested_file = _remove_requested_file, + .is_request_dir = _is_request_dir, }, .connection_id = connection_id, .state = TNC_CONNECTION_STATE_CREATE, @@ -206,6 +284,7 @@ imv_state_t *imv_attestation_state_create(TNC_ConnectionID connection_id) .rec = TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION, .eval = TNC_IMV_EVALUATION_RESULT_DONT_KNOW, .pts = pts_create(FALSE), + .requested_files = linked_list_create(), ); platform_info = lib->settings->get_str(lib->settings, diff --git a/src/libimcv/plugins/imv_attestation/imv_attestation_state.h b/src/libimcv/plugins/imv_attestation/imv_attestation_state.h index 2e83e8216..09bf04c58 100644 --- a/src/libimcv/plugins/imv_attestation/imv_attestation_state.h +++ b/src/libimcv/plugins/imv_attestation/imv_attestation_state.h @@ -25,9 +25,11 @@ #include #include #include +#include typedef struct imv_attestation_state_t imv_attestation_state_t; typedef enum imv_attestation_handshake_state_t imv_attestation_handshake_state_t; +typedef struct file_request_t file_request_t; /** * IMV Attestation Handshake States (state machine) @@ -40,6 +42,14 @@ enum imv_attestation_handshake_state_t { IMV_ATTESTATION_STATE_END, }; +/** + * Defines an structure to hold requested file/directory + */ +struct file_request_t { + int request_id; + int is_dir; +}; + /** * Internal state of an imv_attestation_t connection instance */ @@ -72,6 +82,43 @@ struct imv_attestation_state_t { */ pts_t* (*get_pts)(imv_attestation_state_t *this); + /** + * Add an entry to list of requested files/directories + * + * @param request_id unique request id + * @param is_dir 0 for file and 1 for directory + */ + void (*add_requested_file)(imv_attestation_state_t *this, int request_id, int is_dir); + + /** + * Creates enumerator over the list of requested file/directories + * + * @return enumerator over requested files/directories list + */ + enumerator_t* (*create_requests_enumerator)(imv_attestation_state_t *this); + + /** + * Returns number of entries in the list of requested file/directories + * + * @return number of entries in the list of requested file/directories + */ + int (*get_requests_count)(imv_attestation_state_t *this); + + /** + * Removes an entry with matching request_id from list of requested files/directories + * + * @param request_id unique request id + * @return TRUE if request entry found, FALSE otherwise + */ + bool (*remove_requested_file)(imv_attestation_state_t *this, int request_id); + + /** + * Returns TRUE if entry with given ID is directory and FALSE otherwise + * + * @param request_id unique request id + * @return TRUE if request entry found, FALSE otherwise + */ + bool (*is_request_dir)(imv_attestation_state_t *this, int request_id, bool *is_dir); }; /** diff --git a/src/libimcv/tcg/pts/pts_database.c b/src/libimcv/tcg/pts/pts_database.c index e256dc99e..f38e5dd10 100644 --- a/src/libimcv/tcg/pts/pts_database.c +++ b/src/libimcv/tcg/pts/pts_database.c @@ -54,35 +54,6 @@ METHOD(pts_database_t, create_file_enumerator, enumerator_t*, return e; } -METHOD(pts_database_t, is_directory, bool, - private_pts_database_t *this, int id, bool *is_directory) -{ - enumerator_t *e; - int is_dir; - - /* look for a entry in files table with matching id */ - e = this->db->query(this->db, - "SELECT f.type FROM files AS f " - "WHERE f.id = ?", - DB_INT, id, DB_INT); - - if (!e) - { - DBG1(DBG_TNC, "database enumerator failed", id); - return FALSE; - } - if (!e->enumerate(e, &is_dir)) - { - e->destroy(e); - DBG1(DBG_TNC, "file entry with given id:%d not found", id); - return FALSE; - } - - *is_directory = (is_dir == 1) ? TRUE : FALSE; - return TRUE; -} - - METHOD(pts_database_t, create_files_in_dir_enumerator, enumerator_t*, private_pts_database_t *this, int id) { @@ -97,35 +68,53 @@ METHOD(pts_database_t, create_files_in_dir_enumerator, enumerator_t*, return e; } -METHOD(pts_database_t, create_file_meas_enumerator, enumerator_t*, - private_pts_database_t *this, char *product, int id, pts_meas_algorithms_t algorithm) +METHOD(pts_database_t, check_measurement, bool, + private_pts_database_t *this, chunk_t received_hash, char *product, int id, char *file_name, pts_meas_algorithms_t algorithm, bool is_dir) { enumerator_t *e; + chunk_t db_measurement; + + /* look for all entries belonging to a product, file and directory in file_hashes table */ - /* look for all entries belonging to a product and file in file_hashes table */ - e = this->db->query(this->db, + e = (is_dir) ? this->db->query(this->db, + "SELECT fh.hash FROM file_hashes AS fh " + "JOIN files AS f ON fh.file = f.id " + "JOIN products AS p ON fh.product = p.id " + "WHERE f.path = ? AND p.name = ? AND fh.directory = ? AND fh.algo = ?", + DB_TEXT, file_name, DB_TEXT, product, DB_INT, id, DB_INT, algorithm, DB_BLOB) : + + this->db->query(this->db, "SELECT fh.hash FROM file_hashes AS fh " "JOIN files AS f ON fh.file = f.id " "JOIN products AS p ON fh.product = p.id " "WHERE p.name = ? AND f.id = ? AND fh.algo = ?", DB_TEXT, product, DB_INT, id, DB_INT, algorithm, DB_BLOB); - return e; + + if (!e) + { + DBG1(DBG_TNC, " database enumerator failed"); + return FALSE; + } + if (!e->enumerate(e, &db_measurement)) + { + DBG2(DBG_TNC, " measurement for '%s' not found" + " in database", file_name); + e->destroy(e); + /* Ignore the measurements for which we do not have the hash saved in database */ + return TRUE; + } + if (chunk_equals(db_measurement, received_hash)) + { + DBG2(DBG_TNC, " %#B for '%s' is ok", + &received_hash, file_name); + return TRUE; + } + + DBG1(DBG_IMV, " %#B for '%s' does not match %#B", + &received_hash, file_name, &db_measurement); + return FALSE; } -METHOD(pts_database_t, create_dir_meas_enumerator, enumerator_t*, - private_pts_database_t *this, char *product, int id, char *file_name, pts_meas_algorithms_t algorithm) -{ - enumerator_t *e; - - /* look for all entries belonging to a product, file and directory in file_hashes table */ - e = this->db->query(this->db, - "SELECT fh.hash FROM file_hashes AS fh " - "JOIN files AS f ON fh.file = f.id " - "JOIN products AS p ON fh.product = p.id " - "WHERE f.path = ? AND p.name = ? AND fh.directory = ? AND fh.algo = ?", - DB_TEXT, file_name, DB_TEXT, product, DB_INT, id, DB_INT, algorithm, DB_BLOB); - return e; -} METHOD(pts_database_t, destroy, void, private_pts_database_t *this) @@ -144,10 +133,8 @@ pts_database_t *pts_database_create(char *uri) INIT(this, .public = { .create_file_enumerator = _create_file_enumerator, - .is_directory = _is_directory, .create_files_in_dir_enumerator = _create_files_in_dir_enumerator, - .create_file_meas_enumerator = _create_file_meas_enumerator, - .create_dir_meas_enumerator = _create_dir_meas_enumerator, + .check_measurement = _check_measurement, .destroy = _destroy, }, .db = lib->db->create(lib->db, uri), diff --git a/src/libimcv/tcg/pts/pts_database.h b/src/libimcv/tcg/pts/pts_database.h index d4d595c15..d69b0c1c4 100644 --- a/src/libimcv/tcg/pts/pts_database.h +++ b/src/libimcv/tcg/pts/pts_database.h @@ -40,15 +40,6 @@ struct pts_database_t { */ enumerator_t* (*create_file_enumerator)(pts_database_t *this, char *product); - /** - * Get if file with given id is directory - * - * @id primary key in files table - * @is_directory TRUE if entry with given ID has type of directory - * @return TRUE if query is not failed - */ - bool (*is_directory)(pts_database_t *this, int id, bool *is_directory); - /** * Get Enumerator over files in a given directory with measurements * @@ -57,27 +48,19 @@ struct pts_database_t { */ enumerator_t* (*create_files_in_dir_enumerator)(pts_database_t *this, int id); - /** - * Get Hash measurement of a file with given id and hashing algorithm type - * - * @product software product (os, vpn client, etc.) - * @id primary key in files table - * @algorithm measurement algorithm type - * @return enumerator over all measurements matching a given release - */ - enumerator_t* (*create_file_meas_enumerator)(pts_database_t *this, char *product, - int id, pts_meas_algorithms_t algorithm); /** * Get Hash measurement of a file in a folder with given id and hashing algorithm type * + * @received_hash measurement of a file to match with database entry * @product software product (os, vpn client, etc.) * @id primary key in files table - * @file_name path in files table + * @file_name path in files table, obligatory for the files in directory * @algorithm measurement algorithm type + * @is_dir TRUE if file is requested as content in a directory * @return enumerator over all measurements matching a given release */ - enumerator_t* (*create_dir_meas_enumerator)(pts_database_t *this, char *product, - int id, char *file_name, pts_meas_algorithms_t algorithm); + bool (*check_measurement)(pts_database_t *this, chunk_t received_hash, + char *product, int id, char *file_name, pts_meas_algorithms_t algorithm, bool is_dir); /** @@ -90,7 +73,7 @@ struct pts_database_t { /** * Creates an pts_database_t object * - * @param ur database uri + * @param uri database uri */ pts_database_t* pts_database_create(char *uri);