diff options
author | Cyrille Bagard <nocbos@gmail.com> | 2019-10-10 21:45:56 (GMT) |
---|---|---|
committer | Cyrille Bagard <nocbos@gmail.com> | 2019-10-10 21:45:56 (GMT) |
commit | 96afc0325e1c85bf1a76d63c6441cf8f044708fe (patch) | |
tree | 1cf431fb3a888f9da14c0f3d46ee70bcd336a983 /src/analysis/db/snapshot.c | |
parent | ae1744244fa777535cc707b9e6a014eb281b982c (diff) |
Introduced first steps towards database snapshots.
Diffstat (limited to 'src/analysis/db/snapshot.c')
-rw-r--r-- | src/analysis/db/snapshot.c | 965 |
1 files changed, 965 insertions, 0 deletions
diff --git a/src/analysis/db/snapshot.c b/src/analysis/db/snapshot.c new file mode 100644 index 0000000..d75ac49 --- /dev/null +++ b/src/analysis/db/snapshot.c @@ -0,0 +1,965 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * snapshot.c - prototypes gestion des instantanés de bases de données + * + * Copyright (C) 2019 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide 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 3 of the License, or + * (at your option) any later version. + * + * Chrysalide 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. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#include "snapshot.h" + + +#include <assert.h> +#include <malloc.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <openssl/rand.h> + + +#include <i18n.h> + + +#include "collection.h" +#include "../../common/compression.h" +#include "../../common/extstr.h" +#include "../../common/io.h" +#include "../../common/xml.h" +#include "../../core/logs.h" + + + +/* ------------------------ GESTION UNITAIRE DES INSTANTANES ------------------------ */ + + +/* Caractéristiques d'un instantané */ +typedef struct _snapshot_node_t +{ + struct _snapshot_node_t *parent; /* Parent hiérarchique */ + + char *name; /* Nom de l'instantané */ + char *desc; /* Description associée */ + + char *id; /* Identifiant attribué */ + char *path; /* Fichier extrait */ + + struct _snapshot_node_t **children; /* Sous-noeuds rattachés */ + size_t count; /* Quantité de ces noeuds */ + +} snapshot_node_t; + + +/* Constitue un nouveau noeud d'instantané. */ +static snapshot_node_t *create_snapshot_node(const char *); + +/* Libère la mémoire occupée par un noeud d'instantané. */ +static void destroy_snapshot_node(snapshot_node_t *); + +/* Définit le chemin vers une base de données pour un noeud. */ +static bool setup_snapshot_node_db_path(snapshot_node_t *, const char *, const char *); + +/* Valide la présence d'une base de données pour chaque noeud. */ +static bool check_snapshot_nodes(const snapshot_node_t *); + +/* Enregistre tous les éléments associés aux instantanés. */ +static DBError save_snapshot_node(const snapshot_node_t *, xmlDocPtr, xmlXPathContextPtr, struct archive *); + +/* Recherche le noeud d'instantané lié à un identifiant. */ +static snapshot_node_t *find_snapshot_node(snapshot_node_t *, const char *); + +/* Ajoute un instantané comme prolongement d'un instantané. */ +static void add_snapshot_node(snapshot_node_t *, snapshot_node_t *); + + + +/* --------------------- MANIPULATIONS D'ENSEMBLE D'INSTANTANES --------------------- */ + + +/* Gestionnaire d'instantanés de bases de données (instance) */ +struct _GDbSnapshot +{ + GObject parent; /* A laisser en premier */ + + char *tmpdir; /* Répertoire de travail */ + char *hash; /* Empreinte de binaire */ + + snapshot_node_t *nodes; /* Instantanés présents */ + snapshot_node_t *current; /* Instantané courant */ + +}; + +/* Gestionnaire d'instantanés de bases de données (classe) */ +struct _GDbSnapshotClass +{ + GObjectClass parent; /* A laisser en premier */ + +}; + + +/* Initialise la classe des gestionnaires d'instantanés. */ +static void g_db_snapshot_class_init(GDbSnapshotClass *); + +/* Initialise un gestionnaire d'instantanés de base de données. */ +static void g_db_snapshot_init(GDbSnapshot *); + +/* Supprime toutes les références externes. */ +static void g_db_snapshot_dispose(GDbSnapshot *); + +/* Procède à la libération totale de la mémoire. */ +static void g_db_snapshot_finalize(GDbSnapshot *); + +/* Prépare un gestionnaire d'instantanés de bases de données. */ +static GDbSnapshot *g_db_snapshot_new(const char *, const char *); + + + +/* ---------------------------------------------------------------------------------- */ +/* GESTION UNITAIRE DES INSTANTANES */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : id = éventuel identifiant prédéfini. * +* * +* Description : Constitue un nouveau noeud d'instantané. * +* * +* Retour : Structure mise en place ou NULL en cas d'échec. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static snapshot_node_t *create_snapshot_node(const char *id) +{ + snapshot_node_t *result; /* Nouvel instantané à renvoyer*/ + int ret; /* Bilan d'une génération */ + unsigned char rand[32]; /* Tirage aléatoire */ + size_t i; /* Boucle de parcours */ + char _id[65]; /* Identifiant nouveau */ + + static char *alphabet = "0123456789abcdef"; + + if (id == NULL) + { + ret = RAND_bytes(rand, sizeof(rand)); + + if (ret != 1) + { + LOG_ERROR_OPENSSL; + result = NULL; + goto exit; + } + + for (i = 0; i < sizeof(rand); i++) + { + _id[i * 2 + 0] = alphabet[rand[i] & 0xf]; + _id[i * 2 + 1] = alphabet[(rand[i] >> 4) & 0xf]; + } + + _id[64] = '\0'; + + id = _id; + + } + + result = malloc(sizeof(snapshot_node_t)); + + result->parent = NULL; + + result->name = NULL; + result->desc = NULL; + + result->id = strdup(id); + result->path = NULL; + + result->children = NULL; + result->count = 0; + + exit: + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : node = noeud d'instantané à traiter. * +* * +* Description : Libère la mémoire occupée par un noeud d'instantané. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void destroy_snapshot_node(snapshot_node_t *node) +{ + size_t i; /* Boucle de parcours */ + int ret; /* Bilan d'un appel */ + + for (i = 0; i < node->count; i++) + destroy_snapshot_node(node->children[i]); + + if (node->name != NULL) + free(node->name); + + if (node->desc != NULL) + free(node->desc); + + free(node->id); + + if (node->path != NULL) + { + ret = unlink(node->path); + if (ret != 0) LOG_ERROR_N("unlink"); + + free(node->path); + + } + + if (node->children != NULL) + free(node->children); + + free(node); + +} + + +/****************************************************************************** +* * +* Paramètres : node = noeud d'instantané à traiter. * +* tmpdir = répertoire de travail temporaire. * +* hash = empreinte du binaire à représenter. * +* * +* Description : Définit le chemin vers une base de données pour un noeud. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool setup_snapshot_node_db_path(snapshot_node_t *node, const char *tmpdir, const char *hash) +{ + bool result; /* Bilan à retourner */ + int ret; /* Bilan d'une génération */ + + ret = asprintf(&node->path, "%s" G_DIR_SEPARATOR_S "%s_%s_db.sql", tmpdir, hash, node->id); + result = (ret > 0); + + if (result) + { + ret = ensure_path_exists(node->path); + result = (ret != -1); + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : node = départ du parcours de vérification. * +* * +* Description : Valide la présence d'une base de données pour chaque noeud. * +* * +* Retour : true si l'ensemble de noeuds est dans un état cohérent. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool check_snapshot_nodes(const snapshot_node_t *node) +{ + bool result; /* Bilan à retourner */ + size_t i; /* Boucle de parcours */ + + result = (node->path != NULL); + + if (!result) + log_variadic_message(LMT_ERROR, _("Database is missing for snapshot '%s'"), node->id); + + for (i = 0; i < node->count && result; i++) + result = check_snapshot_nodes(node->children[i]); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : node = départ du parcours. * +* xdoc = document XML à compléter. * +* context = contexte pour les recherches. * +* archive = archive en cours de constitution. * +* * +* Description : Enregistre tous les éléments associés aux instantanés. * +* * +* Retour : Identifiant de l'instantané courant. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static DBError save_snapshot_node(const snapshot_node_t *node, xmlDocPtr xdoc, xmlXPathContextPtr context, struct archive *archive) +{ + DBError result; /* Conclusion à retourner */ + char *name; /* Désignation d'une entrée */ + int ret; /* Bilan d'un appel */ + CPError error; /* Bilan d'une compression */ + xmlNodePtr xml_node; /* Nouveau noeud XML */ + bool status; /* Bilan d'un ajout XML */ + size_t i; /* Boucle de parcours */ + + /* Sauvegarde de la base de données */ + + ret = asprintf(&name, "%s.db", node->id); + + if (ret < 0) + { + result = DBE_SYS_ERROR; + goto exit; + } + + assert(node->path != NULL); + + error = add_file_into_archive(archive, node->path, name); + + free(name); + + switch (error) + { + case CPE_NO_ERROR: + break; + + case CPE_SYSTEM_ERROR: + result = DBE_SYS_ERROR; + goto exit; + break; + + case CPE_ARCHIVE_ERROR: + result = DBE_ARCHIVE_ERROR; + goto exit; + break; + + } + + /* Inscription dans le document XML */ + + xml_node = ensure_node_exist(xdoc, context, "/ChrysalideBinary/Snapshots"); + + if (xml_node == NULL) + { + result = DBE_XML_ERROR; + goto exit; + } + + xml_node = add_node_to_xpath(xdoc, context, "/ChrysalideBinary/Snapshots", "Snapshot"); + + if (xml_node == NULL) + { + result = DBE_XML_ERROR; + goto exit; + } + + status = _add_string_attribute_to_node(xml_node, "id", node->id); + + if (!status) + { + result = DBE_XML_ERROR; + goto exit; + } + + if (node->parent != NULL) + { + status = _add_string_attribute_to_node(xml_node, "parent", node->parent->id); + + if (!status) + { + result = DBE_XML_ERROR; + goto exit; + } + + } + + /* Poursuite des enregistrement */ + + result = DBE_NONE; + + for (i = 0; i < node->count && result == DBE_NONE; i++) + result = save_snapshot_node(node->children[i], xdoc, context, archive); + + exit: + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : node = départ du parcours de recherche. * +* id = identifiant de l'instantané visé. * +* * +* Description : Recherche le noeud d'instantané lié à un identifiant. * +* * +* Retour : Noeud trouvé ou NULL en cas d'échec. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static snapshot_node_t *find_snapshot_node(snapshot_node_t *node, const char *id) +{ + snapshot_node_t *result; /* Noeud trouvé à renvoyer */ + size_t i; /* Boucle de parcours */ + + if (strcmp(node->id, id) == 0) + result = node; + + else + { + result = NULL; + + for (i = 0; i < node->count && result == NULL; i++) + result = find_snapshot_node(node->children[i], id); + + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : node = instantané de rattachement. * +* child = instantané à attacher. * +* * +* Description : Ajoute un instantané comme prolongement d'un instantané. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void add_snapshot_node(snapshot_node_t *node, snapshot_node_t *child) +{ + node->children = realloc(node->children, ++node->count * sizeof(snapshot_node_t *)); + + node->children[node->count - 1] = child; + +} + + + + + +/* ---------------------------------------------------------------------------------- */ +/* MANIPULATIONS D'ENSEMBLE D'INSTANTANES */ +/* ---------------------------------------------------------------------------------- */ + + +/* Indique le type défini pour un gestionnaire d'instantanés de bases de données. */ +G_DEFINE_TYPE(GDbSnapshot, g_db_snapshot, G_TYPE_OBJECT); + + +/****************************************************************************** +* * +* Paramètres : klass = classe à initialiser. * +* * +* Description : Initialise la classe des gestionnaires d'instantanés. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_db_snapshot_class_init(GDbSnapshotClass *klass) +{ + GObjectClass *object; /* Autre version de la classe */ + + object = G_OBJECT_CLASS(klass); + + object->dispose = (GObjectFinalizeFunc/* ! */)g_db_snapshot_dispose; + object->finalize = (GObjectFinalizeFunc)g_db_snapshot_finalize; + +} + + +/****************************************************************************** +* * +* Paramètres : snap = instance à initialiser. * +* * +* Description : Initialise un gestionnaire d'instantanés de base de données. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_db_snapshot_init(GDbSnapshot *snap) +{ + snap->tmpdir = NULL; + snap->hash = NULL; + + snap->nodes = NULL; + + snap->current = NULL; + +} + + +/****************************************************************************** +* * +* Paramètres : snap = instance d'objet GLib à traiter. * +* * +* Description : Supprime toutes les références externes. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_db_snapshot_dispose(GDbSnapshot *snap) +{ + G_OBJECT_CLASS(g_db_snapshot_parent_class)->dispose(G_OBJECT(snap)); + +} + + +/****************************************************************************** +* * +* Paramètres : snap = instance d'objet GLib à traiter. * +* * +* Description : Procède à la libération totale de la mémoire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_db_snapshot_finalize(GDbSnapshot *snap) +{ + if (snap->tmpdir != NULL) + free(snap->tmpdir); + + if (snap->hash != NULL) + free(snap->hash); + + if (snap->nodes != NULL) + destroy_snapshot_node(snap->nodes); + + G_OBJECT_CLASS(g_db_snapshot_parent_class)->finalize(G_OBJECT(snap)); + +} + + +/****************************************************************************** +* * +* Paramètres : tmpdir = répertoire de travail temporaire. * +* hash = empreinte du binaire à représenter. * +* * +* Description : Prépare un gestionnaire d'instantanés de bases de données. * +* * +* Retour : Structure mise en place ou NULL en cas d'échec. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static GDbSnapshot *g_db_snapshot_new(const char *tmpdir, const char *hash) +{ + GDbSnapshot *result; /* Adresse à retourner */ + + result = g_object_new(G_TYPE_DB_SNAPSHOT, NULL); + + result->tmpdir = strdup(tmpdir); + result->hash = strdup(hash); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : tmpdir = répertoire de travail temporaire. * +* hash = empreinte du binaire à représenter. * +* collections = ensemble de modifications par catégories. * +* * +* Description : Prépare un gestionnaire d'instantanés de bases de données. * +* * +* Retour : Structure mise en place ou NULL en cas d'échec. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GDbSnapshot *g_db_snapshot_new_empty(const char *tmpdir, const char *hash, GList *collections) +{ + GDbSnapshot *result; /* Adresse à retourner */ + sqlite3 *db; /* Base de données à manipuler */ + int ret; /* Bilan de la création */ + bool status; /* Bilan d'une mise en place */ + GList *iter; /* Boucle de parcours */ + GDbCollection *collec; /* Collection visée manipulée */ + + result = g_db_snapshot_new(tmpdir, hash); + + result->nodes = create_snapshot_node(NULL); + + status = setup_snapshot_node_db_path(result->nodes, tmpdir, hash); + if (!status) goto error; + + result->current = result->nodes; + + ret = sqlite3_open(result->nodes->path, &db); + + if (ret != SQLITE_OK) + { + LOG_ERROR_SQLITE(db, "sqlite3_open"); + goto error_db; + } + + for (iter = g_list_first(collections); + iter != NULL; + iter = g_list_next(iter)) + { + collec = G_DB_COLLECTION(iter->data); + + status = g_db_collection_create_db_table(collec, db); + if (!status) goto error_db; + + } + + sqlite3_close(db); + + return result; + + error_db: + + sqlite3_close(db); + + error: + + g_object_unref(G_OBJECT(result)); + + return NULL; + +} + + +/****************************************************************************** +* * +* Paramètres : tmpdir = répertoire de travail temporaire. * +* hash = empreinte du binaire à représenter. * +* xdoc = document XML à compléter. * +* context = contexte pour les recherches. * +* * +* Description : Charge un gestionnaire d'instantanés de bases de données. * +* * +* Retour : Structure mise en place ou NULL en cas d'échec. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GDbSnapshot *g_db_snapshot_new_from_xml(const char *tmpdir, const char *hash, xmlDocPtr xdoc, xmlXPathContextPtr context) +{ + GDbSnapshot *result; /* Adresse à retourner */ + xmlXPathObjectPtr xobject; /* Cible d'une recherche */ + size_t count; /* Nombre de contenus premiers */ + size_t i; /* Boucle de parcours */ + xmlNodePtr xml_node; /* Noeud XML avec propriétés */ + char *parent_id; /* Identifiant de noeud parent */ + snapshot_node_t *parent; /* Instantané parent trouvé */ + char *node_id; /* Identifiant de noeud courant*/ + snapshot_node_t *node; /* Instantané nouveau constitué*/ + + result = g_db_snapshot_new(tmpdir, hash); + + /* Chargement de l'ensemble des instantanés */ + + xobject = get_node_xpath_object(context, "/ChrysalideBinary/Snapshots/Snapshot"); + + count = XPATH_OBJ_NODES_COUNT(xobject); + + for (i = 0; i < count; i++) + { + xml_node = NODE_FROM_PATH_OBJ(xobject, i); + + parent_id = qck_get_node_prop_value(xml_node, "parent"); + + if (parent_id == NULL) + parent = NULL; + + else + { + if (result->nodes == NULL) + parent = NULL; + else + parent = find_snapshot_node(result->nodes, parent_id); + + free(parent_id); + + if (parent == NULL) + goto bad_xml; + + } + + node_id = qck_get_node_prop_value(xml_node, "id"); + + if (node_id == NULL) + goto bad_xml; + + node = create_snapshot_node(node_id); + + free(node_id); + + if (node == NULL) + goto bad_xml; + + if (parent == NULL) + { + if (result->nodes != NULL) + goto bad_xml; + + result->nodes = node; + + } + else + add_snapshot_node(parent, node); + + } + + if(xobject != NULL) + xmlXPathFreeObject(xobject); + + /* Détermination de l'instantané courant */ + + node_id = get_node_text_value(context, "/ChrysalideBinary/CurrentSnapshot"); + + if (node_id == NULL) + result->current = result->nodes; + else + { + result->current = find_snapshot_node(result->nodes, node_id); + free(node_id); + } + + if (result->current == NULL) + goto no_current; + + return result; + + bad_xml: + + if(xobject != NULL) + xmlXPathFreeObject(xobject); + + no_current: + + g_object_unref(G_OBJECT(result)); + + return NULL; + +} + + +/****************************************************************************** +* * +* Paramètres : snap = gestionnaire d'instantanés à constituer. * +* archive = archive en cours de lecture. * +* * +* Description : Associe une base de données aux instantanés chargés. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_db_snapshot_fill(GDbSnapshot *snap, struct archive *archive) +{ + bool result; /* Bilan à retourner */ + struct archive_entry *entry; /* Elément de l'archive */ + int ret; /* Bilan d'un appel */ + const char *path; /* Désignation d'un fichier */ + const char *dot; /* Début de l'extension */ + char *node_id; /* Identifiant de noeud */ + snapshot_node_t *node; /* Instantané trouvé */ + + result = false; + + for (ret = archive_read_next_header(archive, &entry); + ret == ARCHIVE_OK; + ret = archive_read_next_header(archive, &entry)) + { + path = archive_entry_pathname(entry); + + if (!_endswith(path, ".db", &dot)) + continue; + + node_id = strndup(path, dot - path); + + node = find_snapshot_node(snap->nodes, node_id); + + free(node_id); + + if (!setup_snapshot_node_db_path(node, snap->tmpdir, snap->hash)) + break; + + if (!dump_archive_entry_into_file(archive, entry, node->path)) + break; + + } + + if (ret != ARCHIVE_EOF) + goto exit; + + if (!check_snapshot_nodes(snap->nodes)) + goto exit; + + result = true; + + exit: + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : snap = gestionnaire d'instantanés à consulter. * +* xdoc = document XML à compléter. * +* context = contexte pour les recherches. * +* archive = archive en cours de constitution. * +* * +* Description : Enregistre tous les éléments associés aux instantanés. * +* * +* Retour : Bilan de l'opération sous forme de code d'erreur. * +* * +* Remarques : - * +* * +******************************************************************************/ + +DBError g_db_snapshot_save(const GDbSnapshot *snap, xmlDocPtr xdoc, xmlXPathContextPtr context, struct archive *archive) +{ + DBError result; /* Conclusion à retourner */ + bool status; /* Bilan d'un ajout XML */ + + assert(snap->current != NULL); + + status = add_content_to_node(xdoc, context, "/ChrysalideBinary/CurrentSnapshot", snap->current->id); + + if (!status) + result = DBE_XML_ERROR; + + else + result = save_snapshot_node(snap->nodes, xdoc, context, archive); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : snap = gestionnaire d'instantanés à consulter. * +* * +* Description : Fournit l'identifiant de l'instanné courant. * +* * +* Retour : Identifiant de l'instantané courant. * +* * +* Remarques : - * +* * +******************************************************************************/ + +const char *g_db_snapshot_get_current_id(const GDbSnapshot *snap) +{ + char *result; /* Indentifiant à retourner */ + + assert(snap->current != NULL); + + if (snap->current == NULL) + result = NULL; + else + result = snap->current->id; + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : snap = gestionnaire d'instantanés à consulter. * +* id = identifiant de l'instantané visé. * +* * +* Description : Fournit la base de données correspondant à instanné donné. * +* * +* Retour : Base de données liée à l'instantané demandé ou NULL. * +* * +* Remarques : - * +* * +******************************************************************************/ + +sqlite3 *g_db_snapshot_get_database(const GDbSnapshot *snap, const char *id) +{ + sqlite3 *result; /* Base SQLite à retourner */ + snapshot_node_t *node; /* Instantané trouvé */ + int ret; /* Bilan d'un appel */ + + node = find_snapshot_node(snap->nodes, id); + + if (node == NULL) + { + log_variadic_message(LMT_ERROR, _("Snapshot not found for id '%s'"), id); + result = NULL; + } + + else + { + ret = sqlite3_open(node->path, &result); + + if (ret != SQLITE_OK) + { + if (result != NULL) + sqlite3_close(result); + + result = NULL; + + } + + } + + return result; + +} |