/* * Copyright (C) 2011-2012 Sansar Choinyambuu, 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 . * * 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_database.h" #include #include typedef struct private_pts_database_t private_pts_database_t; /** * Private data of a pts_database_t object. * */ struct private_pts_database_t { /** * Public pts_database_t interface. */ pts_database_t public; /** * database instance */ database_t *db; }; METHOD(pts_database_t, create_file_meas_enumerator, enumerator_t*, private_pts_database_t *this, char *product) { enumerator_t *e; /* look for all entries belonging to a product in the files table */ e = this->db->query(this->db, "SELECT f.id, f.type, f.path FROM files AS f " "JOIN product_file AS pf ON f.id = pf.file " "JOIN products AS p ON p.id = pf.product " "WHERE p.name = ? AND pf.measurement = 1", DB_TEXT, product, DB_INT, DB_INT, DB_TEXT); return e; } METHOD(pts_database_t, create_file_meta_enumerator, enumerator_t*, private_pts_database_t *this, char *product) { enumerator_t *e; /* look for all entries belonging to a product in the files table */ e = this->db->query(this->db, "SELECT f.type, f.path FROM files AS f " "JOIN product_file AS pf ON f.id = pf.file " "JOIN products AS p ON p.id = pf.product " "WHERE p.name = ? AND pf.metadata = 1", DB_TEXT, product, DB_INT, DB_TEXT); return e; } METHOD(pts_database_t, create_file_hash_enumerator, enumerator_t*, private_pts_database_t *this, char *product, pts_meas_algorithms_t algo, int id, bool is_dir) { enumerator_t *e; if (is_dir) { e = this->db->query(this->db, "SELECT f.path, 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 fh.directory = ? AND fh.algo = ? " "ORDER BY f.path", DB_TEXT, product, DB_INT, id, DB_INT, algo, DB_TEXT, DB_BLOB); } else { e = this->db->query(this->db, "SELECT f.path, 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 fh.file = ? AND fh.algo = ?", DB_TEXT, product, DB_INT, id, DB_INT, algo, DB_TEXT, DB_BLOB); } return e; } METHOD(pts_database_t, check_aik_keyid, status_t, private_pts_database_t *this, chunk_t keyid, int *kid) { enumerator_t *e; /* If the AIK is registered get the primary key */ e = this->db->query(this->db, "SELECT id FROM keys WHERE keyid = ?", DB_BLOB, keyid, DB_INT); if (!e) { DBG1(DBG_PTS, "no database query enumerator returned"); return FAILED; } if (!e->enumerate(e, kid)) { DBG1(DBG_PTS, "AIK %#B is not registered in database", &keyid); e->destroy(e); return FAILED; } e->destroy(e); return SUCCESS; } METHOD(pts_database_t, check_file_measurement, status_t, private_pts_database_t *this, char *product, pts_meas_algorithms_t algo, chunk_t measurement, char *filename) { enumerator_t *e; chunk_t hash; status_t status = NOT_FOUND; e = this->db->query(this->db, "SELECT fh.hash FROM file_hashes AS fh " "JOIN files AS f ON f.id = fh.file " "JOIN products AS p ON p.id = fh.product " "WHERE p.name = ? AND f.path = ? AND fh.algo = ?", DB_TEXT, product, DB_TEXT, filename, DB_INT, algo, DB_BLOB); if (!e) { return FAILED; } while (e->enumerate(e, &hash)) { /* with relative filenames there might be multiple entries */ if (chunk_equals(measurement, hash)) { status = SUCCESS; break; } else { status = VERIFY_ERROR; } } e->destroy(e); return status; } METHOD(pts_database_t, create_comp_evid_enumerator, enumerator_t*, private_pts_database_t *this, int kid) { enumerator_t *e; /* look for all entries belonging to an AIK in the components table */ e = this->db->query(this->db, "SELECT c.vendor_id, c.name, c.qualifier, kc.depth " "FROM components AS c " "JOIN key_component AS kc ON c.id = kc.component " "WHERE kc.key = ? ORDER BY kc.seq_no", DB_INT, kid, DB_INT, DB_INT, DB_INT, DB_INT); return e; } METHOD(pts_database_t, check_comp_measurement, status_t, private_pts_database_t *this, chunk_t measurement, int cid, int kid, int seq_no, int pcr, pts_meas_algorithms_t algo) { enumerator_t *e; chunk_t hash; status_t status = NOT_FOUND; e = this->db->query(this->db, "SELECT hash FROM component_hashes " "WHERE component = ? AND key = ? " "AND seq_no = ? AND pcr = ? AND algo = ? ", DB_INT, cid, DB_INT, kid, DB_INT, seq_no, DB_INT, pcr, DB_INT, algo, DB_BLOB); if (!e) { DBG1(DBG_PTS, "no database query enumerator returned"); return FAILED; } while (e->enumerate(e, &hash)) { if (chunk_equals(hash, measurement)) { status = SUCCESS; break; } else { DBG1(DBG_PTS, "PCR %2d no matching component measurement #%d " "found in database", pcr, seq_no); DBG1(DBG_PTS, " expected: %#B", &hash); DBG1(DBG_PTS, " received: %#B", &measurement); status = VERIFY_ERROR; break; } } e->destroy(e); if (status == NOT_FOUND) { DBG1(DBG_PTS, "PCR %2d no measurement #%d " "found in database", pcr, seq_no); } return status; } METHOD(pts_database_t, insert_comp_measurement, status_t, private_pts_database_t *this, chunk_t measurement, int cid, int kid, int seq_no, int pcr, pts_meas_algorithms_t algo) { int id; if (this->db->execute(this->db, &id, "INSERT INTO component_hashes " "(component, key, seq_no, pcr, algo, hash) " "VALUES (?, ?, ?, ?, ?, ?)", DB_INT, cid, DB_INT, kid, DB_INT, seq_no, DB_INT, pcr, DB_INT, algo, DB_BLOB, measurement) == 1) { return SUCCESS; } DBG1(DBG_PTS, "could not insert component measurement into database"); return FAILED; } METHOD(pts_database_t, delete_comp_measurements, int, private_pts_database_t *this, int cid, int kid) { return this->db->execute(this->db, NULL, "DELETE FROM component_hashes " "WHERE component = ? AND key = ?", DB_INT, cid, DB_INT, kid); } METHOD(pts_database_t, get_comp_measurement_count, status_t, private_pts_database_t *this, pts_comp_func_name_t *comp_name, chunk_t keyid, pts_meas_algorithms_t algo, int *cid, int *kid, int *count) { enumerator_t *e; status_t status = SUCCESS; /* Initialize count */ *count = 0; if (_check_aik_keyid(this, keyid, kid) != SUCCESS) { return FAILED; } /* Get the primary key of the Component Functional Name */ e = this->db->query(this->db, "SELECT id FROM components " " WHERE vendor_id = ? AND name = ? AND qualifier = ?", DB_INT, comp_name->get_vendor_id(comp_name), DB_INT, comp_name->get_name(comp_name), DB_INT, comp_name->get_qualifier(comp_name), DB_INT); if (!e) { DBG1(DBG_PTS, "no database query enumerator returned"); return FAILED; } if (!e->enumerate(e, cid)) { DBG1(DBG_PTS, "component functional name not found in database"); e->destroy(e); return FAILED; } e->destroy(e); /* Get the number of stored measurements for a given AIK and component */ e = this->db->query(this->db, "SELECT COUNT(*) FROM component_hashes AS ch " "WHERE component = ? AND key = ? AND algo = ?", DB_INT, *cid, DB_INT, *kid, DB_INT, algo, DB_INT); if (!e) { DBG1(DBG_PTS, "no database query enumerator returned"); return FAILED; } if (!e->enumerate(e, count)) { DBG1(DBG_PTS, "no component measurement count returned from database"); status = FAILED; } e->destroy(e); return status; } METHOD(pts_database_t, destroy, void, private_pts_database_t *this) { this->db->destroy(this->db); free(this); } /** * See header */ pts_database_t *pts_database_create(char *uri) { private_pts_database_t *this; INIT(this, .public = { .create_file_meas_enumerator = _create_file_meas_enumerator, .create_file_meta_enumerator = _create_file_meta_enumerator, .create_comp_evid_enumerator = _create_comp_evid_enumerator, .create_file_hash_enumerator = _create_file_hash_enumerator, .check_aik_keyid = _check_aik_keyid, .check_file_measurement = _check_file_measurement, .check_comp_measurement = _check_comp_measurement, .insert_comp_measurement = _insert_comp_measurement, .delete_comp_measurements = _delete_comp_measurements, .get_comp_measurement_count = _get_comp_measurement_count, .destroy = _destroy, }, .db = lib->db->create(lib->db, uri), ); if (!this->db) { DBG1(DBG_PTS, "failed to connect to PTS file measurement database '%s'", uri); free(this); return NULL; } return &this->public; }