/* * Copyright (C) 2011 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 "attest_db.h" typedef struct private_attest_db_t private_attest_db_t; /** * Private data of an attest_db_t object. */ struct private_attest_db_t { /** * Public members of attest_db_state_t */ attest_db_t public; /** * Software product to be queried */ char *product; /** * Primary key of software product to be queried */ int pid; /** * TRUE if product has been set */ bool product_set; /** * Measurement file to be queried */ char *file; /** * Primary key of measurement file to be queried */ int fid; /** * TRUE if file has been set */ bool file_set; /** * Directory containing the Measurement file to be queried */ char *dir; /** * Primary key of the directory to be queried */ int did; /** * TRUE if directory has been set */ bool dir_set; /** * File measurement hash algorithm */ pts_meas_algorithms_t algo; /** * Attestation database */ database_t *db; }; METHOD(attest_db_t, set_product, bool, private_attest_db_t *this, char *product, bool create) { enumerator_t *e; if (this->product_set) { printf("product has already been set\n"); return FALSE; } this->product = strdup(product); e = this->db->query(this->db, "SELECT id FROM products WHERE name = ?", DB_TEXT, product, DB_INT); if (e) { if (e->enumerate(e, &this->pid)) { this->product_set = TRUE; } e->destroy(e); } if (this->product_set) { return TRUE; } if (!create) { printf("product '%s' not found in database\n", product); } /* Add a new database entry */ this->product_set = this->db->execute(this->db, &this->pid, "INSERT INTO products (name) VALUES (?)", DB_TEXT, product); printf("product '%s' %sinserted into database\n", product, this->product_set ? "" : "could not be "); return this->product_set; } METHOD(attest_db_t, set_pid, bool, private_attest_db_t *this, int pid) { enumerator_t *e; char *product; if (this->product_set) { printf("product has already been set\n"); return FALSE; } this->pid = pid; e = this->db->query(this->db, "SELECT name FROM products WHERE id = ?", DB_INT, pid, DB_TEXT); if (e) { if (e->enumerate(e, &product)) { this->product = strdup(product); this->product_set = TRUE; } else { printf("no product found with pid %d in database\n", pid); } e->destroy(e); } return this->product_set; } METHOD(attest_db_t, set_file, bool, private_attest_db_t *this, char *file, bool create) { enumerator_t *e; if (this->file_set) { printf("file has already been set\n"); return FALSE; } this->file = strdup(file); e = this->db->query(this->db, "SELECT id FROM files WHERE path = ?", DB_TEXT, file, DB_INT); if (e) { if (e->enumerate(e, &this->fid)) { this->file_set = TRUE; } e->destroy(e); } if (this->file_set) { return TRUE; } if (!create) { printf("file '%s' not found in database\n", file); } /* Add a new database entry */ this->file_set = this->db->execute(this->db, &this->fid, "INSERT INTO files (type, path) VALUES (0, ?)", DB_TEXT, file); printf("file '%s' %sinserted into database\n", file, this->file_set ? "" : "could not be "); return this->file_set; } METHOD(attest_db_t, set_fid, bool, private_attest_db_t *this, int fid) { enumerator_t *e; char *file; if (this->file_set) { printf("file has already been set\n"); return FALSE; } this->fid = fid; e = this->db->query(this->db, "SELECT path FROM files WHERE id = ?", DB_INT, fid, DB_TEXT); if (e) { if (e->enumerate(e, &file)) { this->file = strdup(file); this->file_set = TRUE; } else { printf("no file found with fid %d\n", fid); } e->destroy(e); } return this->file_set; } METHOD(attest_db_t, set_directory, bool, private_attest_db_t *this, char *dir, bool create) { enumerator_t *e; if (this->dir_set) { printf("directory has already been set\n"); return FALSE; } free(this->dir); this->dir = strdup(dir); e = this->db->query(this->db, "SELECT id FROM files WHERE type = 1 AND path = ?", DB_TEXT, dir, DB_INT); if (e) { if (e->enumerate(e, &this->did)) { this->dir_set = TRUE; } e->destroy(e); } if (this->dir_set) { return TRUE; } if (!create) { printf("directory '%s' not found in database\n", dir); } /* Add a new database entry */ this->dir_set = this->db->execute(this->db, &this->did, "INSERT INTO files (type, path) VALUES (1, ?)", DB_TEXT, dir); printf("directory '%s' %sinserted into database\n", dir, this->dir_set ? "" : "could not be "); return this->dir_set; } METHOD(attest_db_t, set_did, bool, private_attest_db_t *this, int did) { enumerator_t *e; char *dir; if (this->dir_set) { printf("directory has already been set\n"); return FALSE; } this->did = did; e = this->db->query(this->db, "SELECT path FROM files WHERE id = ?", DB_INT, did, DB_TEXT); if (e) { if (e->enumerate(e, &dir)) { free(this->dir); this->dir = strdup(dir); this->dir_set = TRUE; } else { printf("no directory found with did %d\n", did); } e->destroy(e); } return this->dir_set; } METHOD(attest_db_t, set_algo, void, private_attest_db_t *this, pts_meas_algorithms_t algo) { this->algo = algo; } METHOD(attest_db_t, list_files, void, private_attest_db_t *this) { enumerator_t *e; char *file; int fid, is_dir, count = 0; if (this->pid) { 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 " "WHERE pf.product = ? ORDER BY f.path", DB_INT, this->pid, DB_INT, DB_INT, DB_TEXT); } else { e = this->db->query(this->db, "SELECT id, type, path FROM files " "ORDER BY path", DB_INT, DB_INT, DB_TEXT); } if (e) { while (e->enumerate(e, &fid, &is_dir, &file)) { printf("%3d: %s %s\n", fid, is_dir ? "d":" ", file); count++; } e->destroy(e); printf("%d file%s found", count, (count == 1) ? "" : "s"); if (this->product) { printf(" for product '%s'", this->product); } printf("\n"); } } METHOD(attest_db_t, list_products, void, private_attest_db_t *this) { enumerator_t *e; char *product; int pid, count = 0; if (this->fid) { e = this->db->query(this->db, "SELECT p.id, p.name FROM products AS p " "JOIN product_file AS pf ON p.id = pf.product " "WHERE pf.file = ? ORDER BY p.name", DB_INT, this->fid, DB_INT, DB_TEXT); } else { e = this->db->query(this->db, "SELECT id, name FROM products " "ORDER BY name", DB_INT, DB_TEXT); } if (e) { while (e->enumerate(e, &pid, &product)) { printf("%3d: %s\n", pid, product); count++; } e->destroy(e); printf("%d product%s found", count, (count == 1) ? "" : "s"); if (this->file) { printf(" for file '%s'", this->file); } printf("\n"); } } /** * get the directory if there is one from the files tables */ static void get_directory(private_attest_db_t *this, int did, char **directory) { enumerator_t *e; char *dir; free(*directory); *directory = strdup(""); if (did) { e = this->db->query(this->db, "SELECT path from files WHERE id = ?", DB_INT, did, DB_TEXT); if (e) { if (e->enumerate(e, &dir)) { free(*directory); *directory = strdup(dir); } e->destroy(e); } } } static bool slash(char *directory, char *file) { return *file != '/' && directory[max(0, strlen(directory)-1)] != '/'; } METHOD(attest_db_t, list_hashes, void, private_attest_db_t *this) { enumerator_t *e; chunk_t hash; char *file, *dir, *product; int fid, fid_old = 0, did, did_old = 0, count = 0; dir = strdup(""); if (this->pid && this->fid) { e = this->db->query(this->db, "SELECT hash FROM file_hashes " "WHERE algo = ? AND file = ? AND directory = ? AND product = ?", DB_INT, this->algo, DB_INT, this->fid, DB_INT, this->did, DB_INT, this->pid, DB_BLOB); if (e) { while (e->enumerate(e, &hash)) { if (this->fid != fid_old) { printf("%3d: %s%s%s\n", this->fid, this->dir, slash(this->dir, this->file) ? "/" : "", this->file); fid_old = this->fid; } printf(" %#B\n", &hash); count++; } e->destroy(e); printf("%d %N value%s found for product '%s'\n", count, hash_algorithm_names, pts_meas_algo_to_hash(this->algo), (count == 1) ? "" : "s", this->product); } } else if (this->pid) { e = this->db->query(this->db, "SELECT f.id, f. f.path, fh.hash, fh.directory " "FROM file_hashes AS fh " "JOIN files AS f ON f.id = fh.file " "WHERE fh.algo = ? AND fh.product = ? " "ORDER BY fh.directory, f.path", DB_INT, this->algo, DB_INT, this->pid, DB_INT, DB_TEXT, DB_BLOB, DB_INT); if (e) { while (e->enumerate(e, &fid, &file, &hash, &did)) { if (fid != fid_old || did != did_old) { if (did != did_old) { get_directory(this, did, &dir); } printf("%3d: %s%s%s\n", fid, dir, slash(dir, file) ? "/" : "", file); fid_old = fid; did_old = did; } printf(" %#B\n", &hash); count++; } e->destroy(e); printf("%d %N value%s found for product '%s'\n", count, hash_algorithm_names, pts_meas_algo_to_hash(this->algo), (count == 1) ? "" : "s", this->product); } } else if (this->fid) { e = this->db->query(this->db, "SELECT p.name, fh.hash, fh.directory " "FROM file_hashes AS fh " "JOIN products AS p ON p.id = fh.product " "WHERE fh.algo = ? AND fh.file = ? AND fh.directory = ?" "ORDER BY p.name", DB_INT, this->algo, DB_INT, this->fid, DB_INT, this->did, DB_TEXT, DB_BLOB, DB_INT); if (e) { while (e->enumerate(e, &product, &hash, &did)) { printf("%#B '%s'\n", &hash, product); count++; } e->destroy(e); printf("%d %N value%s found for file '%s%s%s'\n", count, hash_algorithm_names, pts_meas_algo_to_hash(this->algo), (count == 1) ? "" : "s", this->dir, slash(this->dir, this->file) ? "/" : "", this->file); } } else { e = this->db->query(this->db, "SELECT f.id, f.path, p.name, fh.hash, fh.directory " "FROM file_hashes AS fh " "JOIN files AS f ON f.id = fh.file " "JOIN products AS p ON p.id = fh.product " "WHERE fh.algo = ? " "ORDER BY fh.directory, f.path, p.name", DB_INT, this->algo, DB_INT, DB_TEXT, DB_TEXT, DB_BLOB, DB_INT); if (e) { while (e->enumerate(e, &fid, &file, &product, &hash, &did)) { if (fid != fid_old || did != did_old) { if (did != did_old) { get_directory(this, did, &dir); did_old = did; } printf("%3d: %s%s%s\n", fid, dir, slash(dir, file) ? "/" : "", file); fid_old = fid; } printf(" %#B '%s'\n", &hash, product); count++; } e->destroy(e); printf("%d %N value%s found\n", count, hash_algorithm_names, pts_meas_algo_to_hash(this->algo), (count == 1) ? "" : "s"); } } free(dir); } METHOD(attest_db_t, add, bool, private_attest_db_t *this) { return FALSE; } METHOD(attest_db_t, delete, bool, private_attest_db_t *this) { return FALSE; } METHOD(attest_db_t, destroy, void, private_attest_db_t *this) { DESTROY_IF(this->db); free(this->product); free(this->file); free(this->dir); free(this); } /** * Described in header. */ attest_db_t *attest_db_create(char *uri) { private_attest_db_t *this; INIT(this, .public = { .set_product = _set_product, .set_pid = _set_pid, .set_file = _set_file, .set_fid = _set_fid, .set_directory = _set_directory, .set_did = _set_did, .set_algo = _set_algo, .list_products = _list_products, .list_files = _list_files, .list_hashes = _list_hashes, .add = _add, .delete = _delete, .destroy = _destroy, }, .dir = strdup(""), .algo = PTS_MEAS_ALGO_SHA256, .db = lib->db->create(lib->db, uri), ); if (!this->db) { fprintf(stderr, "opening database failed.\n"); destroy(this); return NULL; } return &this->public; }