diff options
Diffstat (limited to 'src/analysis/db')
41 files changed, 4938 insertions, 1593 deletions
diff --git a/src/analysis/db/Makefile.am b/src/analysis/db/Makefile.am index 3edfb09..b9325e0 100644 --- a/src/analysis/db/Makefile.am +++ b/src/analysis/db/Makefile.am @@ -3,32 +3,34 @@ noinst_LTLIBRARIES = libanalysisdb.la libanalysisdb_la_SOURCES = \ + admin.h admin.c \ + analyst.h analyst.c \ auth.h auth.c \ + backend-int.h \ + backend.h backend.c \ cdb.h cdb.c \ certs.h certs.c \ + client-int.h \ client.h client.c \ collection-int.h \ collection.h collection.c \ + controller.h controller.c \ item-int.h \ item.h item.c \ protocol.h \ server.h server.c \ snapshot.h snapshot.c +libanalysisdb_la_CFLAGS = $(TOOLKIT_CFLAGS) $(LIBXML_CFLAGS) $(LIBARCHIVE_CFLAGS) $(LIBSQLITE_CFLAGS) $(LIBSSL_CFLAGS) + libanalysisdb_la_LIBADD = \ items/libanalysisdbitems.la \ misc/libanalysisdbmisc.la -libanalysisdb_la_LDFLAGS = $(LIBSSL_LIBS) - devdir = $(includedir)/chrysalide/$(subdir:src/%=core/%) dev_HEADERS = $(libanalysisdb_la_SOURCES:%c=) -AM_CPPFLAGS = $(LIBGTK_CFLAGS) $(LIBXML_CFLAGS) $(LIBARCHIVE_CFLAGS) $(LIBSQLITE_CFLAGS) $(LIBSSL_CFLAGS) - -AM_CFLAGS = $(DEBUG_CFLAGS) $(WARNING_FLAGS) $(COMPLIANCE_FLAGS) - SUBDIRS = items misc diff --git a/src/analysis/db/admin.c b/src/analysis/db/admin.c new file mode 100644 index 0000000..024cbed --- /dev/null +++ b/src/analysis/db/admin.c @@ -0,0 +1,501 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * admin.c - connexion en administrateur à un serveur Chrysalide + * + * Copyright (C) 2014-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 "admin.h" + + +#include <assert.h> +#include <malloc.h> +#include <poll.h> +#include <string.h> + + +#include "client-int.h" +#include "../../common/leb128.h" +#include "../../core/logs.h" + + + +/* Description de client à l'écoute (instance) */ +struct _GAdminClient +{ + GHubClient parent; /* A laisser en premier */ + + char **binaries; /* Liste de binaires existants*/ + size_t binaries_count; /* Taille de cette liste */ + GMutex binaries_lock; /* Concurrence des accès */ + +}; + +/* Description de client à l'écoute (classe) */ +struct _GAdminClientClass +{ + GHubClientClass parent; /* A laisser en premier */ + + /* Signaux */ + + void (* existing_binaries_updated) (GAdminClient *); + +}; + + +/* Initialise la classe des descriptions de fichier binaire. */ +static void g_admin_client_class_init(GAdminClientClass *); + +/* Initialise une description de fichier binaire. */ +static void g_admin_client_init(GAdminClient *); + +/* Supprime toutes les références externes. */ +static void g_admin_client_dispose(GAdminClient *); + +/* Procède à la libération totale de la mémoire. */ +static void g_admin_client_finalize(GAdminClient *); + +/* Assure l'accueil des nouvelles mises à jour. */ +static void *g_admin_client_update(GAdminClient *); + +/* Met à jour la liste des binaires existants. */ +static bool g_admin_client_update_existing_binaries(GAdminClient *, packed_buffer_t *); + + + +/* Indique le type défini pour une description de client à l'écoute. */ +G_DEFINE_TYPE(GAdminClient, g_admin_client, G_TYPE_HUB_CLIENT); + + +/****************************************************************************** +* * +* Paramètres : klass = classe à initialiser. * +* * +* Description : Initialise la classe des descriptions de fichier binaire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_admin_client_class_init(GAdminClientClass *klass) +{ + GObjectClass *object; /* Autre version de la classe */ + GHubClientClass *client; /* Classe parente */ + + object = G_OBJECT_CLASS(klass); + + object->dispose = (GObjectFinalizeFunc/* ! */)g_admin_client_dispose; + object->finalize = (GObjectFinalizeFunc)g_admin_client_finalize; + + client = G_HUB_CLIENT_CLASS(klass); + + client->role = CRL_ADMIN; + client->recv_func = (GThreadFunc)g_admin_client_update; + + g_signal_new("existing-binaries-updated", + G_TYPE_ADMIN_CLIENT, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GAdminClientClass, existing_binaries_updated), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + +} + + +/****************************************************************************** +* * +* Paramètres : client = instance à initialiser. * +* * +* Description : Initialise une description de fichier binaire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_admin_client_init(GAdminClient *client) +{ + client->binaries = NULL; + client->binaries_count = 0; + g_mutex_init(&client->binaries_lock); + +} + + +/****************************************************************************** +* * +* Paramètres : archive = instance d'objet GLib à traiter. * +* * +* Description : Supprime toutes les références externes. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_admin_client_dispose(GAdminClient *client) +{ + g_hub_client_stop(G_HUB_CLIENT(client)); + + G_OBJECT_CLASS(g_admin_client_parent_class)->dispose(G_OBJECT(client)); + +} + + +/****************************************************************************** +* * +* Paramètres : client = instance d'objet GLib à traiter. * +* * +* Description : Procède à la libération totale de la mémoire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_admin_client_finalize(GAdminClient *client) +{ + size_t i; /* Boucle de parcours */ + + if (client->binaries != NULL) + { + for (i = 0; i < client->binaries_count; i++) + free(client->binaries[i]); + + free(client->binaries); + + } + + g_mutex_clear(&client->binaries_lock); + + G_OBJECT_CLASS(g_admin_client_parent_class)->finalize(G_OBJECT(client)); + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Prépare un client pour une connexion à une BD. * +* * +* Retour : Structure mise en place ou NULL en cas d'échec. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GAdminClient *g_admin_client_new(void) +{ + GAdminClient *result; /* Adresse à retourner */ + + result = g_object_new(G_TYPE_ADMIN_CLIENT, NULL); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : client = client pour les accès distants à manipuler. * +* * +* Description : Assure l'accueil des nouvelles mises à jour. * +* * +* Retour : NULL. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void *g_admin_client_update(GAdminClient *client) +{ + GHubClient *base; /* Base de l'instance */ + struct pollfd fds[2]; /* Surveillance des flux */ + packed_buffer_t in_pbuf; /* Tampon de réception */ + int ret; /* Bilan d'un appel */ + bool status; /* Bilan d'une opération */ + uint32_t command; /* Commande de la requête */ + //packed_buffer_t out_pbuf; /* Tampon d'émission */ + char *msg; /* Message d'erreur à imprimer */ + + base = G_HUB_CLIENT(client); + + /** + * Phase d'écoute continue... + */ + + fds[0].fd = base->stop_ctrl[0]; + fds[0].events = POLLIN | POLLPRI; + + fds[1].fd = SSL_get_fd(base->tls_fd); + fds[1].events = POLLIN | POLLPRI; + + init_packed_buffer(&in_pbuf); + + while (true) + { + ret = poll(fds, 2, -1); + if (ret == -1) + { + LOG_ERROR_N("poll"); + break; + } + + /* Demande expresse d'arrêt des procédures */ + if (fds[0].revents) + break; + + /* Le canal est fermé, une sortie doit être demandée... */ + if (fds[1].revents & POLLNVAL) + break; + + /** + * Même chose, cf. "TCP: When is EPOLLHUP generated?" + * https://stackoverflow.com/questions/52976152/tcp-when-is-epollhup-generated/52976327#52976327 + */ + + if (fds[1].revents & (POLLHUP | POLLRDHUP)) + break; + + if (fds[1].revents & (POLLIN | POLLPRI)) + { + reset_packed_buffer(&in_pbuf); + + status = ssl_recv_packed_buffer(&in_pbuf, base->tls_fd); + if (!status) goto bad_exchange; + + next_command: + + status = extract_packed_buffer(&in_pbuf, &command, sizeof(uint32_t), true); + if (!status) goto bad_exchange; + + switch (command) + { + case DBC_EXISTING_BINARIES: + status = g_admin_client_update_existing_binaries(client, &in_pbuf); + if (!status) goto bad_exchange; + break; + + default: + log_variadic_message(LMT_INFO, + _("This command is not available on this side: 0x%08x"), command); + goto bad_exchange; + break; + + } + + if (has_more_data_in_packed_buffer(&in_pbuf)) + goto next_command; + + continue; + + bad_exchange: + + asprintf(&msg, _("Bad reception from %s"), base->desc); + + LOG_ERROR(LMT_ERROR, msg); + + free(msg); + + break; + + } + + } + + g_hub_client_stop(G_HUB_CLIENT(client)); + + exit_packed_buffer(&in_pbuf); + + return NULL; + +} + + +/****************************************************************************** +* * +* Paramètres : client = client pour les accès distants à manipuler. * +* * +* Description : Effectue une demande de liste de binaires existants. * +* * +* Retour : true si la commande a bien été envoyée, false sinon. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_admin_client_request_existing_binaries(GAdminClient *client) +{ + bool result; /* Bilan partiel à remonter */ + packed_buffer_t out_pbuf; /* Tampon d'émission */ + SSL *tls_fd; /* Canal de communication SSL */ + + init_packed_buffer(&out_pbuf); + + tls_fd = g_hub_client_get_ssl_fd(G_HUB_CLIENT(client)); + + if (tls_fd == NULL) + result = false; + + else + { + result = extend_packed_buffer(&out_pbuf, (uint32_t []) { DBC_LIST_BINARIES }, sizeof(uint32_t), true); + + if (result) + result = ssl_send_packed_buffer(&out_pbuf, tls_fd); + + g_hub_client_put_ssl_fd(G_HUB_CLIENT(client), tls_fd); + + } + + exit_packed_buffer(&out_pbuf); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : client = client pour les accès distants à manipuler. * +* pbuf = données présentes à traiter. * +* * +* Description : Met à jour la liste des binaires existants. * +* * +* Retour : true si l'opération s'est déroulée sans encombre, ou false. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool g_admin_client_update_existing_binaries(GAdminClient *client, packed_buffer_t *pbuf) +{ + bool result; /* Validité à retourner */ + size_t i; /* Boucle de parcours */ + uleb128_t count; /* Nombre d'éléments détectés */ + rle_string name; /* Nom à exporter */ + + result = true; + + g_mutex_lock(&client->binaries_lock); + + if (client->binaries != NULL) + { + for (i = 0; i < client->binaries_count; i++) + free(client->binaries[i]); + + free(client->binaries); + + client->binaries = NULL; + client->binaries_count = 0; + + } + + result = unpack_uleb128(&count, pbuf); + if (!result) goto exit; + + client->binaries_count = count; + + if (count > 0) + { + client->binaries = calloc(client->binaries_count, sizeof(char *)); + + for (i = 0; i < client->binaries_count; i++) + { + result = unpack_rle_string(&name, pbuf); + if (!result) break; + + client->binaries[i] = strdup(name.data); + + exit_rle_string(&name); + + } + + if (i < client->binaries_count) + { + for (i = 0; i < client->binaries_count; i++) + if (client->binaries[i] != NULL) + free(client->binaries[i]); + + free(client->binaries); + + client->binaries = NULL; + client->binaries_count = 0; + + } + + } + + exit: + + g_mutex_unlock(&client->binaries_lock); + + if (result) + g_signal_emit_by_name(client, "existing-binaries-updated"); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : client = client pour les accès distants à manipuler. * +* count = taille de la liste retournée. [OUT] * +* * +* Description : Fournit la liste des instantanés existants. * +* * +* Retour : Liste de binaires en place ou NULL si aucun. * +* * +* Remarques : - * +* * +******************************************************************************/ + +char **g_admin_client_get_existing_binaries(GAdminClient *client, size_t *count) +{ + char **result; /* Liste à retourner */ + size_t i; /* Boucle de parcours */ + + result = NULL; + *count = 0; + + g_mutex_lock(&client->binaries_lock); + + if (client->binaries_count > 0) + { + result = malloc(client->binaries_count * sizeof(char *)); + *count = client->binaries_count; + + for (i = 0; i < client->binaries_count; i++) + result[i] = strdup(client->binaries[i]); + + } + + g_mutex_unlock(&client->binaries_lock); + + return result; + +} diff --git a/src/analysis/db/admin.h b/src/analysis/db/admin.h new file mode 100644 index 0000000..31366ae --- /dev/null +++ b/src/analysis/db/admin.h @@ -0,0 +1,68 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * admin.h - prototypes pour la connexion en administrateur à un serveur Chrysalide + * + * Copyright (C) 2021 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/>. + */ + + +#ifndef _ANALYSIS_DB_ADMIN_H +#define _ANALYSIS_DB_ADMIN_H + + +#include <glib-object.h> +#include <stdbool.h> +#include <openssl/ssl.h> + + +#include "client.h" +#include "collection.h" +#include "misc/snapshot.h" + + + +#define G_TYPE_ADMIN_CLIENT g_admin_client_get_type() +#define G_ADMIN_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_ADMIN_CLIENT, GAdminClient)) +#define G_IS_ADMIN_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_ADMIN_CLIENT)) +#define G_ADMIN_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_ADMIN_CLIENT, GAdminClientClass)) +#define G_IS_ADMIN_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_ADMIN_CLIENT)) +#define G_ADMIN_CLIENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_ADMIN_CLIENT, GAdminClientClass)) + + +/* Description de client à l'écoute (instance) */ +typedef struct _GAdminClient GAdminClient; + +/* Description de client à l'écoute (classe) */ +typedef struct _GAdminClientClass GAdminClientClass; + + +/* Indique le type défini pour une description de client à l'écoute. */ +GType g_admin_client_get_type(void); + +/* Prépare un client pour une connexion à une BD. */ +GAdminClient *g_admin_client_new(void); + +/* Effectue une demande de liste de binaires existants. */ +bool g_admin_client_request_existing_binaries(GAdminClient *); + +/* Fournit la liste des instantanés existants. */ +char **g_admin_client_get_existing_binaries(GAdminClient *, size_t *); + + + +#endif /* _ANALYSIS_DB_ADMIN_H */ diff --git a/src/analysis/db/analyst-int.h b/src/analysis/db/analyst-int.h new file mode 100644 index 0000000..4f76eff --- /dev/null +++ b/src/analysis/db/analyst-int.h @@ -0,0 +1,76 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * analyst-int.h - prototypes pour la définition interne des connexions en analyste à un serveur Chrysalide + * + * Copyright (C) 2022 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/>. + */ + + +#ifndef _ANALYSIS_DB_ANALYST_INT_H +#define _ANALYSIS_DB_ANALYST_INT_H + + +#include "analyst.h" +#include "client-int.h" + + + +/* Description de client à l'écoute (instance) */ +struct _GAnalystClient +{ + GHubClient parent; /* A laisser en premier */ + + char *cnt_hash; /* Empreinte du binaire lié */ + char *cnt_class; /* Interprétation du contenu */ + + GLoadedContent *loaded; /* Contenu chargé */ + GList *collections; /* Collections d'un binaire */ + + bool can_get_updates; /* Réception de maj possibles ?*/ + + snapshot_info_t *snapshots; /* Liste des instantanés */ + size_t snap_count; /* Taille de cette liste */ + GMutex snap_lock; /* Concurrence des accès */ + + snapshot_id_t current; /* Instantané courant */ + bool has_current; /* Validité de l'identifiant */ + GMutex cur_lock; /* Concurrence des accès */ + +}; + +/* Description de client à l'écoute (classe) */ +struct _GAnalystClientClass +{ + GHubClientClass parent; /* A laisser en premier */ + + /* Signaux */ + + void (* ready) (GAnalystClient *); + void (* server_status_changed) (GAnalystClient *, LoadingStatusHint); + void (* snapshots_updated) (GAnalystClient *); + void (* snapshot_changed) (GAnalystClient *); + +}; + + +/* Prépare un client pour une connexion à une BD. */ +bool g_analyst_client_setup(GAnalystClient *, const char *, const char *, GList *, GLoadedContent *); + + + +#endif /* _ANALYSIS_DB_ANALYST_INT_H */ diff --git a/src/analysis/db/analyst.c b/src/analysis/db/analyst.c new file mode 100644 index 0000000..43fb840 --- /dev/null +++ b/src/analysis/db/analyst.c @@ -0,0 +1,1418 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * analyst.c - connexion en analyste à un serveur Chrysalide + * + * Copyright (C) 2014-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 "analyst.h" + + +#include <assert.h> +#include <poll.h> +#include <string.h> + + +#include "analyst-int.h" +#include "../storage/storage.h" +#include "../../core/logs.h" + + + +/* ----------------------- DEFINITION D'ANALYSTE COMME CLIENT ----------------------- */ + + +/* Initialise la classe des descriptions de fichier binaire. */ +static void g_analyst_client_class_init(GAnalystClientClass *); + +/* Initialise une description de fichier binaire. */ +static void g_analyst_client_init(GAnalystClient *); + +/* Supprime toutes les références externes. */ +static void g_analyst_client_dispose(GAnalystClient *); + +/* Procède à la libération totale de la mémoire. */ +static void g_analyst_client_finalize(GAnalystClient *); + +/* Termine la constitution des données initiales à présenter. */ +static bool g_analyst_client_complete_hello(GAnalystClient *, packed_buffer_t *); + +/* Assure l'accueil des nouvelles mises à jour. */ +static void *g_analyst_client_update(GAnalystClient *); + +/* Met à jour la liste des instantanés courants. */ +static bool g_analyst_client_update_snapshots(GAnalystClient *, packed_buffer_t *); + +/* Met à jour l'identifiant de l'instantané courant. */ +static bool g_analyst_client_update_current_snapshot(GAnalystClient *, packed_buffer_t *); + + + +/* ------------------------- PRISES EN COMPTE DES COMMANDES ------------------------- */ + + +/* Prend en compte une évolution du statut côté serveur. */ +static bool g_analyst_client_handle_loading_hints(GAnalystClient *, packed_buffer_t *); + + + +/* ---------------------------------------------------------------------------------- */ +/* GLUES POUR LA GLIB */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Définit un type GLib pour l'énumération "LoadingStatusHint". * +* * +* Retour : Type GLib enregistré. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GType g_loading_status_hint_type(void) +{ + static GType result = 0; + + static const GEnumValue values[] = { + { LSH_READY, "LSH_READY", "ready" }, + { LSH_ON_WAIT_LIST, "LSH_ON_WAIT_LIST", "on_wait_list" }, + { LSH_NEED_CONTENT, "LSH_NEED_CONTENT", "need_content" }, + { LSH_NEED_FORMAT, "LSH_NEED_FORMAT", "need_format" }, + { LSH_NEED_ARCH, "LSH_NEED_ARCH", "need_arch" }, + { 0, NULL, NULL } + }; + + if (result == 0) + result = g_enum_register_static(g_intern_static_string("LoadingStatusHint"), values); + + return result; + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* DEFINITION D'ANALYSTE COMME CLIENT */ +/* ---------------------------------------------------------------------------------- */ + + +/* Indique le type défini pour une description de client à l'écoute. */ +G_DEFINE_TYPE(GAnalystClient, g_analyst_client, G_TYPE_HUB_CLIENT); + + +/****************************************************************************** +* * +* Paramètres : klass = classe à initialiser. * +* * +* Description : Initialise la classe des descriptions de fichier binaire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_analyst_client_class_init(GAnalystClientClass *klass) +{ + GObjectClass *object; /* Autre version de la classe */ + GHubClientClass *client; /* Classe parente */ + + object = G_OBJECT_CLASS(klass); + + object->dispose = (GObjectFinalizeFunc/* ! */)g_analyst_client_dispose; + object->finalize = (GObjectFinalizeFunc)g_analyst_client_finalize; + + client = G_HUB_CLIENT_CLASS(klass); + + client->role = CRL_ANALYST; + client->complete_hello = (complete_client_hello_fc)g_analyst_client_complete_hello; + client->recv_func = (GThreadFunc)g_analyst_client_update; + + g_signal_new("ready", + G_TYPE_ANALYST_CLIENT, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GAnalystClientClass, ready), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + g_signal_new("server-status-changed", + G_TYPE_ANALYST_CLIENT, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GAnalystClientClass, server_status_changed), + NULL, NULL, + g_cclosure_marshal_VOID__ENUM, + G_TYPE_NONE, 1, G_TYPE_LOADING_STATUS_HINT); + + g_signal_new("snapshots-updated", + G_TYPE_ANALYST_CLIENT, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GAnalystClientClass, snapshots_updated), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + g_signal_new("snapshot-changed", + G_TYPE_ANALYST_CLIENT, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GAnalystClientClass, snapshot_changed), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + +} + + +/****************************************************************************** +* * +* Paramètres : client = instance à initialiser. * +* * +* Description : Initialise une description de fichier binaire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_analyst_client_init(GAnalystClient *client) +{ + client->cnt_hash = NULL; + client->cnt_class = NULL; + + client->loaded = NULL; + client->collections = NULL; + + client->can_get_updates = false; + + client->snapshots = NULL; + client->snap_count = 0; + g_mutex_init(&client->snap_lock); + + setup_empty_snapshot_id(&client->current); + client->has_current = false; + g_mutex_init(&client->cur_lock); + +} + + +/****************************************************************************** +* * +* Paramètres : archive = instance d'objet GLib à traiter. * +* * +* Description : Supprime toutes les références externes. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_analyst_client_dispose(GAnalystClient *client) +{ + g_hub_client_stop(G_HUB_CLIENT(client)); + + g_mutex_clear(&client->cur_lock); + + g_mutex_clear(&client->snap_lock); + + g_clear_object(&client->loaded); + + G_OBJECT_CLASS(g_analyst_client_parent_class)->dispose(G_OBJECT(client)); + +} + + +/****************************************************************************** +* * +* Paramètres : client = instance d'objet GLib à traiter. * +* * +* Description : Procède à la libération totale de la mémoire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_analyst_client_finalize(GAnalystClient *client) +{ + size_t i; /* Boucle de parcours */ + + if (client->cnt_hash != NULL) + free(client->cnt_hash); + + if (client->cnt_class != NULL) + free(client->cnt_class); + + if (client->snapshots != NULL) + { + for (i = 0; i < client->snap_count; i++) + exit_snapshot_info(&client->snapshots[i]); + + free(client->snapshots); + + } + + G_OBJECT_CLASS(g_analyst_client_parent_class)->finalize(G_OBJECT(client)); + +} + + +/****************************************************************************** +* * +* Paramètres : hash = empreinte d'un binaire en cours d'analyse. * +* class = nature de l'interprétation de ce contenu. * +* collections = ensemble de collections existantes. * +* loaded = éventuel élément local préchargé. * +* * +* Description : Met en place un client pour une connexion à une BD. * +* * +* Retour : Structure mise en place ou NULL en cas d'échec. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GAnalystClient *g_analyst_client_new(const char *hash, const char *class, GList *collections, GLoadedContent *loaded) +{ + GAnalystClient *result; /* Adresse à retourner */ + bool status; /* Bilan de l'initialisation */ + + result = g_object_new(G_TYPE_ANALYST_CLIENT, NULL); + + status = g_analyst_client_setup(result, hash, class, collections, loaded); + + assert(status); + + if (!status) + g_clear_object(&result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : client = client pour les accès distants à initialiser. * +* hash = empreinte d'un binaire en cours d'analyse. * +* class = nature de l'interprétation de ce contenu. * +* collections = ensemble de collections existantes. * +* loaded = éventuel élément local préchargé. * +* * +* Description : Prépare un client pour une connexion à une BD. * +* * +* Retour : Structure mise en place ou NULL en cas d'échec. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_analyst_client_setup(GAnalystClient *client, const char *hash, const char *class, GList *collections, GLoadedContent *loaded) +{ + bool result; /* Bilan à retourner */ + + result = true; + + client->cnt_hash = strdup(hash); + client->cnt_class = strdup(class); + + client->loaded = loaded; + if (loaded != NULL) g_object_ref(G_OBJECT(loaded)); + + client->collections = collections; + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : client = client pour les accès distants à manipuler. * +* pbuf = tampon d'émission initial à compléter. * +* * +* Description : Termine la constitution des données initiales à présenter. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool g_analyst_client_complete_hello(GAnalystClient *client, packed_buffer_t *pbuf) +{ + bool result; /* Bilan à retourner */ + rle_string str; /* Chaîne à communiquer */ + + init_static_rle_string(&str, client->cnt_hash); + + result = pack_rle_string(&str, pbuf); + + exit_rle_string(&str); + + init_static_rle_string(&str, client->cnt_class); + + result = pack_rle_string(&str, pbuf); + + exit_rle_string(&str); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : client = client pour les accès distants à manipuler. * +* * +* Description : Assure l'accueil des nouvelles mises à jour. * +* * +* Retour : NULL. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void *g_analyst_client_update(GAnalystClient *client) +{ + GHubClient *base; /* Base de l'instance */ + packed_buffer_t out_pbuf; /* Tampon d'émission */ + bool status; /* Bilan d'une opération */ + struct pollfd fds[2]; /* Surveillance des flux */ + packed_buffer_t in_pbuf; /* Tampon de réception */ + int ret; /* Bilan d'un appel */ + uint32_t tmp32; /* Valeur sur 32 bits */ + uint32_t command; /* Commande de la requête */ + DBError error; /* Bilan d'une commande passée */ + GDbCollection *collec; /* Collection visée au final */ + uint8_t tmp8; /* Valeur sur 8 bits */ + char *msg; /* Message d'erreur à imprimer */ + + base = G_HUB_CLIENT(client); + + /** + * Avant toute chose, on demande un stage d'actualisation ! + */ + + init_packed_buffer(&out_pbuf); + + status = extend_packed_buffer(&out_pbuf, (uint32_t []) { DBC_GET_SNAPSHOTS }, sizeof(uint32_t), true); + if (!status) + { + exit_packed_buffer(&out_pbuf); + goto exit; + } + + status = extend_packed_buffer(&out_pbuf, (uint32_t []) { DBC_GET_CUR_SNAPSHOT }, sizeof(uint32_t), true); + if (!status) + { + exit_packed_buffer(&out_pbuf); + goto exit; + } + + status = extend_packed_buffer(&out_pbuf, (uint32_t []) { DBC_GET_ALL_ITEMS }, sizeof(uint32_t), true); + if (!status) + { + exit_packed_buffer(&out_pbuf); + goto exit; + } + + status = ssl_send_packed_buffer(&out_pbuf, base->tls_fd); + if (!status) + { + log_simple_message(LMT_INFO, _("Failed to get all updates")); + exit_packed_buffer(&out_pbuf); + goto exit; + } + + exit_packed_buffer(&out_pbuf); + + /** + * Phase d'écoute continue... + */ + + fds[0].fd = base->stop_ctrl[0]; + fds[0].events = POLLIN | POLLPRI; + + fds[1].fd = SSL_get_fd(base->tls_fd); + fds[1].events = POLLIN | POLLPRI; + + init_packed_buffer(&in_pbuf); + + while (true) + { + ret = poll(fds, 2, -1); + if (ret == -1) + { + LOG_ERROR_N("poll"); + break; + } + + /* Demande expresse d'arrêt des procédures */ + if (fds[0].revents) + break; + + /* Le canal est fermé, une sortie doit être demandée... */ + if (fds[1].revents & POLLNVAL) + break; + + /** + * Même chose, cf. "TCP: When is EPOLLHUP generated?" + * https://stackoverflow.com/questions/52976152/tcp-when-is-epollhup-generated/52976327#52976327 + */ + + if (fds[1].revents & (POLLHUP | POLLRDHUP)) + break; + + if (fds[1].revents & (POLLIN | POLLPRI)) + { + reset_packed_buffer(&in_pbuf); + + status = ssl_recv_packed_buffer(&in_pbuf, base->tls_fd); + if (!status) goto gdcu_bad_exchange; + + next_command: + + status = extract_packed_buffer(&in_pbuf, &command, sizeof(uint32_t), true); + if (!status) goto gdcu_bad_exchange; + + switch (command) + { + case DBC_LOADING_STATUS: + status = g_analyst_client_handle_loading_hints(client, &in_pbuf); + if (!status) goto gdcu_bad_exchange; + break; + + case DBC_SAVE: + + status = extract_packed_buffer(&in_pbuf, &tmp32, sizeof(uint32_t), true); + if (!status) goto gdcu_bad_exchange; + + error = tmp32; + + if (error == DBE_NONE) + log_variadic_message(LMT_INFO, _("Archive saved for binary '%s'"), client->cnt_hash); + else + log_variadic_message(LMT_ERROR, _("Failed to save the archive for binary '%s'"), + client->cnt_hash); + + break; + + case DBC_COLLECTION: + + status = extract_packed_buffer(&in_pbuf, &tmp32, sizeof(uint32_t), true); + if (!status) goto gdcu_bad_exchange; + + collec = find_collection_in_list(client->collections, tmp32); + if (collec == NULL) goto gdcu_bad_exchange; + + if (client->can_get_updates) + status = g_db_collection_unpack(collec, &in_pbuf, NULL); + else + status = _g_db_collection_unpack(collec, &in_pbuf, (DBAction []) { 0 }, NULL); + + if (!status) goto gdcu_bad_exchange; + + break; + + case DBC_GET_ALL_ITEMS: + log_variadic_message(LMT_INFO, + _("This command is not available on this side: 0x%08x"), command); + goto gdcu_bad_exchange; + break; + + case DBC_SET_ALL_ITEMS: + + status = extract_packed_buffer(&in_pbuf, &tmp8, sizeof(uint8_t), true); + if (!status) goto gdcu_bad_exchange; + + client->can_get_updates = (tmp8 == 0x1); + break; + + case DBC_GET_SNAPSHOTS: + log_variadic_message(LMT_INFO, + _("This command is not available on this side: 0x%08x"), command); + goto gdcu_bad_exchange; + break; + + case DBC_SNAPSHOTS_UPDATED: + + status = g_analyst_client_update_snapshots(client, &in_pbuf); + if (!status) goto gdcu_bad_exchange; + + break; + + case DBC_GET_CUR_SNAPSHOT: + log_variadic_message(LMT_INFO, + _("This command is not available on this side: 0x%08x"), command); + goto gdcu_bad_exchange; + break; + + case DBC_CUR_SNAPSHOT_UPDATED: + + status = g_analyst_client_update_current_snapshot(client, &in_pbuf); + if (!status) goto gdcu_bad_exchange; + + break; + + case DBC_SET_CUR_SNAPSHOT: + case DBC_SET_SNAPSHOT_NAME: + case DBC_SET_SNAPSHOT_DESC: + case DBC_CREATE_SNAPSHOT: + case DBC_REMOVE_SNAPSHOT: + log_variadic_message(LMT_INFO, + _("This command is not available on this side: 0x%08x"), command); + goto gdcu_bad_exchange; + break; + + } + + if (has_more_data_in_packed_buffer(&in_pbuf)) + goto next_command; + + client->can_get_updates = true; + continue; + + gdcu_bad_exchange: + + asprintf(&msg, _("Bad reception from %s"), base->desc); + + LOG_ERROR(LMT_ERROR, msg); + + free(msg); + + break; + + } + + } + + exit: + + g_hub_client_stop(G_HUB_CLIENT(client)); + + exit_packed_buffer(&in_pbuf); + + return NULL; + +} + + +/****************************************************************************** +* * +* Paramètres : client = client pour les accès distants à manipuler. * +* pbuf = données présentes à traiter. * +* * +* Description : Met à jour la liste des instantanés courants. * +* * +* Retour : true si l'opération s'est déroulée sans encombre, ou false. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool g_analyst_client_update_snapshots(GAnalystClient *client, packed_buffer_t *pbuf) +{ + bool result; /* Validité à retourner */ + size_t i; /* Boucle de parcours */ + char id[SNAP_ID_HEX_SZ]; /* Caractères hexadécimaux */ + snapshot_info_t info; /* Description d'instantané */ + snapshot_info_t *dest; /* Destination de description */ + + result = true; + + g_mutex_lock(&client->snap_lock); + + if (client->snapshots != NULL) + { + for (i = 0; i < client->snap_count; i++) + exit_snapshot_info(&client->snapshots[i]); + + free(client->snapshots); + + client->snapshots = NULL; + client->snap_count = 0; + + } + + do + { + result = peek_packed_buffer(pbuf, id, SNAP_ID_HEX_SZ, false); + if (!result) break; + + if (strncmp(id, SNAPSHOT_END_MARK, SNAP_ID_HEX_SZ) == 0) + { + advance_packed_buffer(pbuf, SNAP_ID_HEX_SZ); + break; + } + + else + { + setup_empty_snapshot_info(&info); + + result = unpack_snapshot_info(&info, pbuf); + if (!result) break; + + client->snapshots = realloc(client->snapshots, ++client->snap_count * sizeof(snapshot_info_t)); + + dest = &client->snapshots[client->snap_count - 1]; + + setup_empty_snapshot_info(dest); + copy_snapshot_info(dest, &info); + + exit_snapshot_info(&info); + + } + + } + while (true); + + g_mutex_unlock(&client->snap_lock); + + if (result) + g_signal_emit_by_name(client, "snapshots-updated"); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : client = client pour les accès distants à manipuler. * +* pbuf = données présentes à traiter. * +* * +* Description : Met à jour l'identifiant de l'instantané courant. * +* * +* Retour : true si l'opération s'est déroulée sans encombre, ou false. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool g_analyst_client_update_current_snapshot(GAnalystClient *client, packed_buffer_t *pbuf) +{ + bool result; /* Validité à retourner */ + snapshot_id_t id; /* Identifiant d'instantané */ + + setup_empty_snapshot_id(&id); + + result = unpack_snapshot_id(&id, pbuf); + + if (result) + { + g_mutex_lock(&client->cur_lock); + + copy_snapshot_id(&client->current, &id); + client->has_current = true; + + g_mutex_unlock(&client->cur_lock); + + g_signal_emit_by_name(client, "snapshot-changed"); + + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : client = client pour les accès distants à manipuler. * +* content = contenu binaire à envoyer. * +* * +* Description : Envoie un contenu binaire pour conservation côté serveur. * +* * +* Retour : true si la commande a bien été envoyée, false sinon. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_analyst_client_send_content(GAnalystClient *client, GBinContent *content) +{ + bool result; /* Bilan partiel à remonter */ + const gchar *hash; /* Empreinte du contenu fourni */ + packed_buffer_t cnt_pbuf; /* Tampon de stockage */ + GObjectStorage *storage; /* Gestionnaire de stockage */ + off64_t pos; /* Emplacement du binaire */ + SSL *tls_fd; /* Canal de communication SSL */ + packed_buffer_t out_pbuf; /* Tampon d'émission */ + + result = false; + + /* Validation de la conformité du contenu */ + + hash = g_binary_content_get_checksum(content); + + if (strcmp(hash, client->cnt_hash) != 0) + { + log_variadic_message(LMT_ERROR, _("Provided ontent does not match client content (hash: '%s')"), + client->cnt_hash); + goto exit; + } + + /* Conversion en format de stockage */ + + init_packed_buffer(&cnt_pbuf); + + storage = g_object_storage_new(client->cnt_hash); + + result = g_object_storage_store_object(storage, "contents", G_SERIALIZABLE_OBJECT(content), &pos); + if (!result) goto exit_with_failure; + + result = pack_uleb128((uleb128_t []){ pos }, &cnt_pbuf); + if (!result) goto exit_with_failure; + + result = g_object_storage_store(storage, &cnt_pbuf); + if (!result) goto exit_with_failure; + + /* Transmission */ + + tls_fd = g_hub_client_get_ssl_fd(G_HUB_CLIENT(client)); + + if (tls_fd == NULL) + result = false; + + else + { + init_packed_buffer(&out_pbuf); + + result = extend_packed_buffer(&out_pbuf, (uint32_t []) { DBC_SET_CONTENT }, sizeof(uint32_t), true); + + if (result) + result = pack_uleb128((uleb128_t []){ get_packed_buffer_payload_length(&cnt_pbuf) }, &out_pbuf); + + if (result) + result = include_packed_buffer(&out_pbuf, &cnt_pbuf); + + if (result) + result = ssl_send_packed_buffer(&out_pbuf, tls_fd); + + g_hub_client_put_ssl_fd(G_HUB_CLIENT(client), tls_fd); + + exit_packed_buffer(&out_pbuf); + + } + + exit_with_failure: + + g_object_unref(G_OBJECT(storage)); + + exit_packed_buffer(&cnt_pbuf); + + exit: + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : client = client pour les accès distants à manipuler. * +* * +* Description : Effectue une demande de sauvegarde de l'état courant. * +* * +* Retour : true si la commande a bien été envoyée, false sinon. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_analyst_client_save(GAnalystClient *client) +{ + bool result; /* Bilan partiel à remonter */ + packed_buffer_t out_pbuf; /* Tampon d'émission */ + SSL *tls_fd; /* Canal de communication SSL */ + + init_packed_buffer(&out_pbuf); + + tls_fd = g_hub_client_get_ssl_fd(G_HUB_CLIENT(client)); + + if (tls_fd == NULL) + result = false; + + else + { + result = extend_packed_buffer(&out_pbuf, (uint32_t []) { DBC_SAVE }, sizeof(uint32_t), true); + + if (result) + result = ssl_send_packed_buffer(&out_pbuf, tls_fd); + + g_hub_client_put_ssl_fd(G_HUB_CLIENT(client), tls_fd); + + } + + exit_packed_buffer(&out_pbuf); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : client = client pour les accès distants à manipuler. * +* item = élémnent à pousser vers un serveur de collection. * +* * +* Description : Ajoute un élément à la collection d'un serveur. * +* * +* Retour : true si la commande a bien été envoyée, false sinon. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_analyst_client_add_item(GAnalystClient *client, const GDbItem *item) +{ + bool result; /* Bilan partiel à remonter */ + packed_buffer_t out_pbuf; /* Tampon d'émission */ + SSL *tls_fd; /* Canal de communication SSL */ + DBFeatures feature; /* Domaine de fonctionnalité */ + GDbCollection *collec; /* Collection visée au final */ + + init_packed_buffer(&out_pbuf); + + tls_fd = g_hub_client_get_ssl_fd(G_HUB_CLIENT(client)); + + if (tls_fd == NULL) + result = false; + + else + { + feature = g_db_item_get_feature(item); + + collec = find_collection_in_list(client->collections, feature); + if (collec == NULL) + { + result = false; + goto bad_item_feature; + } + + result = g_db_collection_pack(collec, &out_pbuf, DBA_ADD_ITEM, item); + + if (result) + result = ssl_send_packed_buffer(&out_pbuf, tls_fd); + + bad_item_feature: + + g_hub_client_put_ssl_fd(G_HUB_CLIENT(client), tls_fd); + + } + + exit_packed_buffer(&out_pbuf); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : client = client pour les accès distants à manipuler. * +* timestamp = date du dernier élément à garder comme actif. * +* * +* Description : Active les éléments en amont d'un horodatage donné. * +* * +* Retour : true si la commande a bien été envoyée, false sinon. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_analyst_client_set_last_active(GAnalystClient *client, timestamp_t timestamp) +{ + bool result; /* Bilan partiel à remonter */ + packed_buffer_t out_pbuf; /* Tampon d'émission */ + SSL *tls_fd; /* Canal de communication SSL */ + + init_packed_buffer(&out_pbuf); + + tls_fd = g_hub_client_get_ssl_fd(G_HUB_CLIENT(client)); + + if (tls_fd == NULL) + result = false; + + else + { + result = extend_packed_buffer(&out_pbuf, (uint32_t []) { DBC_SET_LAST_ACTIVE }, sizeof(uint32_t), true); + + if (result) + result = pack_timestamp(×tamp, &out_pbuf); + + if (result) + result = ssl_send_packed_buffer(&out_pbuf, tls_fd); + + g_hub_client_put_ssl_fd(G_HUB_CLIENT(client), tls_fd); + + } + + exit_packed_buffer(&out_pbuf); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : client = client pour les accès distants à manipuler. * +* info = description des instantanés présents. [OUT] * +* count = taille de la liste retournée. [OUT] * +* * +* Description : Fournit la liste des instantanés existants. * +* * +* Retour : true si la liste retournée est valide, false sinon. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_analyst_client_get_snapshots(GAnalystClient *client, snapshot_info_t **info, size_t *count) +{ + bool result; /* Validité à retourner */ + size_t i; /* Boucle de parcours */ + snapshot_info_t *dest; /* Destination de description */ + + g_mutex_lock(&client->snap_lock); + + result = (client->snap_count > 0); + + if (result) + { + *info = malloc(client->snap_count * sizeof(snapshot_info_t)); + *count = client->snap_count; + + for (i = 0; i < client->snap_count; i++) + { + dest = &(*info)[i]; + + setup_empty_snapshot_info(dest); + copy_snapshot_info(dest, &client->snapshots[i]); + + } + + } + + g_mutex_unlock(&client->snap_lock); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : client = client pour les accès distants à manipuler. * +* id = identifiant d'instantané à renseigner. [OUT] * +* * +* Description : Fournit l'identifiant de l'instantané courant. * +* * +* Retour : true si l'identifiant retourné est valide, false sinon. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_analyst_client_get_current_snapshot(GAnalystClient *client, snapshot_id_t *id) +{ + bool result; /* Validité à retourner */ + + g_mutex_lock(&client->cur_lock); + + result = client->has_current; + + if (result) + copy_snapshot_id(id, &client->current); + + g_mutex_unlock(&client->cur_lock); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : client = client pour les accès distants à manipuler. * +* id = identifiant d'instantané à activer. * +* * +* Description : Définit l'identifiant de l'instantané courant. * +* * +* Retour : true si la commande a bien été envoyée, false sinon. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_analyst_client_set_current_snapshot(GAnalystClient *client, const snapshot_id_t *id) +{ + bool result; /* Bilan partiel à remonter */ + packed_buffer_t out_pbuf; /* Tampon d'émission */ + SSL *tls_fd; /* Canal de communication SSL */ + + init_packed_buffer(&out_pbuf); + + tls_fd = g_hub_client_get_ssl_fd(G_HUB_CLIENT(client)); + + if (tls_fd == NULL) + result = false; + + else + { + result = extend_packed_buffer(&out_pbuf, (uint32_t []) { DBC_SET_CUR_SNAPSHOT }, sizeof(uint32_t), true); + + if (result) + result = pack_snapshot_id(id, &out_pbuf); + + if (result) + result = ssl_send_packed_buffer(&out_pbuf, tls_fd); + + g_hub_client_put_ssl_fd(G_HUB_CLIENT(client), tls_fd); + + } + + exit_packed_buffer(&out_pbuf); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : client = client pour les accès distants à manipuler. * +* id = identifiant d'instantané à traiter. * +* name = désignation humaine pour l'instantané. * +* * +* Description : Définit la désignation d'un instantané donné. * +* * +* Retour : true si la commande a bien été envoyée, false sinon. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_analyst_client_set_snapshot_name(GAnalystClient *client, const snapshot_id_t *id, const char *name) +{ + bool result; /* Bilan partiel à remonter */ + packed_buffer_t out_pbuf; /* Tampon d'émission */ + SSL *tls_fd; /* Canal de communication SSL */ + rle_string string; /* Chaîne à transmettre */ + + init_packed_buffer(&out_pbuf); + + tls_fd = g_hub_client_get_ssl_fd(G_HUB_CLIENT(client)); + + if (tls_fd == NULL) + result = false; + + else + { + result = extend_packed_buffer(&out_pbuf, (uint32_t []) { DBC_SET_SNAPSHOT_NAME }, sizeof(uint32_t), true); + + if (result) + result = pack_snapshot_id(id, &out_pbuf); + + if (result) + { + init_static_rle_string(&string, name); + + result = pack_rle_string(&string, &out_pbuf); + + exit_rle_string(&string); + + } + + if (result) + result = ssl_send_packed_buffer(&out_pbuf, tls_fd); + + g_hub_client_put_ssl_fd(G_HUB_CLIENT(client), tls_fd); + + } + + exit_packed_buffer(&out_pbuf); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : client = client pour les accès distants à manipuler. * +* id = identifiant d'instantané à traiter. * +* desc = description humaine pour l'instantané. * +* * +* Description : Définit la description d'un instantané donné. * +* * +* Retour : true si la commande a bien été envoyée, false sinon. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_analyst_client_set_snapshot_desc(GAnalystClient *client, const snapshot_id_t *id, const char *desc) +{ + bool result; /* Bilan partiel à remonter */ + packed_buffer_t out_pbuf; /* Tampon d'émission */ + SSL *tls_fd; /* Canal de communication SSL */ + rle_string string; /* Chaîne à transmettre */ + + init_packed_buffer(&out_pbuf); + + tls_fd = g_hub_client_get_ssl_fd(G_HUB_CLIENT(client)); + + if (tls_fd == NULL) + result = false; + + else + { + result = extend_packed_buffer(&out_pbuf, (uint32_t []) { DBC_SET_SNAPSHOT_DESC }, sizeof(uint32_t), true); + + if (result) + result = pack_snapshot_id(id, &out_pbuf); + + if (result) + { + init_static_rle_string(&string, desc); + + result = pack_rle_string(&string, &out_pbuf); + + exit_rle_string(&string); + + } + + if (result) + result = ssl_send_packed_buffer(&out_pbuf, tls_fd); + + g_hub_client_put_ssl_fd(G_HUB_CLIENT(client), tls_fd); + + } + + exit_packed_buffer(&out_pbuf); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : client = client pour les accès distants à manipuler. * +* id = identifiant d'instantané à traiter. * +* * +* Description : Restaure un ancien instantané. * +* * +* Retour : true si la commande a bien été envoyée, false sinon. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_analyst_client_restore_snapshot(GAnalystClient *client, const snapshot_id_t *id) +{ + bool result; /* Bilan partiel à remonter */ + packed_buffer_t out_pbuf; /* Tampon d'émission */ + SSL *tls_fd; /* Canal de communication SSL */ + + init_packed_buffer(&out_pbuf); + + tls_fd = g_hub_client_get_ssl_fd(G_HUB_CLIENT(client)); + + if (tls_fd == NULL) + result = false; + + else + { + result = extend_packed_buffer(&out_pbuf, (uint32_t []) { DBC_SET_CUR_SNAPSHOT }, sizeof(uint32_t), true); + + if (result) + result = pack_snapshot_id(id, &out_pbuf); + + if (result) + result = ssl_send_packed_buffer(&out_pbuf, tls_fd); + + g_hub_client_put_ssl_fd(G_HUB_CLIENT(client), tls_fd); + + } + + exit_packed_buffer(&out_pbuf); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : client = client pour les accès distants à manipuler. * +* * +* Description : Crée un nouvel instantané à partir d'un autre. * +* * +* Retour : true si la commande a bien été envoyée, false sinon. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_analyst_client_create_snapshot(GAnalystClient *client) +{ + bool result; /* Bilan partiel à remonter */ + packed_buffer_t out_pbuf; /* Tampon d'émission */ + SSL *tls_fd; /* Canal de communication SSL */ + + init_packed_buffer(&out_pbuf); + + tls_fd = g_hub_client_get_ssl_fd(G_HUB_CLIENT(client)); + + if (tls_fd == NULL) + result = false; + + else + { + result = extend_packed_buffer(&out_pbuf, (uint32_t []) { DBC_CREATE_SNAPSHOT }, sizeof(uint32_t), true); + + if (result) + result = ssl_send_packed_buffer(&out_pbuf, tls_fd); + + g_hub_client_put_ssl_fd(G_HUB_CLIENT(client), tls_fd); + + } + + exit_packed_buffer(&out_pbuf); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : client = client pour les accès distants à manipuler. * +* id = identifiant d'instantané à traiter. * +* rec = programme une suppression récursive. * +* * +* Description : Supprime un ancien instantané. * +* * +* Retour : true si la commande a bien été envoyée, false sinon. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_analyst_client_remove_snapshot(GAnalystClient *client, const snapshot_id_t *id, bool rec) +{ + bool result; /* Bilan partiel à remonter */ + packed_buffer_t out_pbuf; /* Tampon d'émission */ + SSL *tls_fd; /* Canal de communication SSL */ + + init_packed_buffer(&out_pbuf); + + tls_fd = g_hub_client_get_ssl_fd(G_HUB_CLIENT(client)); + + if (tls_fd == NULL) + result = false; + + else + { + result = extend_packed_buffer(&out_pbuf, (uint32_t []) { DBC_REMOVE_SNAPSHOT }, sizeof(uint32_t), true); + + if (result) + result = pack_snapshot_id(id, &out_pbuf); + + if (result) + result = extend_packed_buffer(&out_pbuf, (uint8_t []) { rec ? 0x1 : 0x0 }, sizeof(uint8_t), false); + + if (result) + result = ssl_send_packed_buffer(&out_pbuf, tls_fd); + + g_hub_client_put_ssl_fd(G_HUB_CLIENT(client), tls_fd); + + } + + exit_packed_buffer(&out_pbuf); + + return result; + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* PRISES EN COMPTE DES COMMANDES */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : archive = archive à connecter avec un utilisateur. * +* in_pbuf = paquet à consulter. * +* * +* Description : Prend en compte une évolution du statut côté serveur. * +* * +* Retour : Indication pour le maintien de la communication. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool g_analyst_client_handle_loading_hints(GAnalystClient *client, packed_buffer_t *in_pbuf) +{ + bool result; /* Bilan à retourner */ + uleb128_t hint; /* Indication du serveur */ + + result = unpack_uleb128(&hint, in_pbuf); + + switch (hint) + { + case LSH_READY: + g_signal_emit_by_name(client, "ready"); + break; + + case LSH_ON_WAIT_LIST: + log_simple_message(LMT_INFO, _("Waiting for content from server...")); + break; + + case LSH_NEED_CONTENT: + case LSH_NEED_FORMAT: + case LSH_NEED_ARCH: + g_signal_emit_by_name(client, "server-status-changed", hint); + break; + + default: + log_variadic_message(LMT_ERROR, + _("Unknown loaded hint received (%x); unsupported newer protocol?"), + hint); + result = false; + break; + + } + + return result; + +} diff --git a/src/analysis/db/analyst.h b/src/analysis/db/analyst.h new file mode 100644 index 0000000..ff189ba --- /dev/null +++ b/src/analysis/db/analyst.h @@ -0,0 +1,115 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * analyst.h - prototypes pour la connexion en analyste à un serveur Chrysalide + * + * Copyright (C) 2014-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/>. + */ + + +#ifndef _ANALYSIS_DB_ANALYST_H +#define _ANALYSIS_DB_ANALYST_H + + +#include <glib-object.h> +#include <stdbool.h> +#include <openssl/ssl.h> + + +#include "client.h" +#include "collection.h" +#include "misc/snapshot.h" +#include "../content.h" +#include "../loaded.h" + + + + +/* ------------------------------- GLUES POUR LA GLIB ------------------------------- */ + + +#define G_TYPE_LOADING_STATUS_HINT g_loading_status_hint_type() + + +/* Définit un type GLib pour l'énumération "LoadingStatusHint". */ +GType g_loading_status_hint_type(void); + + + +/* ----------------------- DEFINITION D'ANALYSTE COMME CLIENT ----------------------- */ + + +#define G_TYPE_ANALYST_CLIENT g_analyst_client_get_type() +#define G_ANALYST_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_ANALYST_CLIENT, GAnalystClient)) +#define G_IS_ANALYST_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_ANALYST_CLIENT)) +#define G_ANALYST_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_ANALYST_CLIENT, GAnalystClientClass)) +#define G_IS_ANALYST_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_ANALYST_CLIENT)) +#define G_ANALYST_CLIENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_ANALYST_CLIENT, GAnalystClientClass)) + + +/* Description de client à l'écoute (instance) */ +typedef struct _GAnalystClient GAnalystClient; + +/* Description de client à l'écoute (classe) */ +typedef struct _GAnalystClientClass GAnalystClientClass; + + +/* Indique le type défini pour une description de client à l'écoute. */ +GType g_analyst_client_get_type(void); + +/* Met en place un client pour une connexion à une BD. */ +GAnalystClient *g_analyst_client_new(const char *, const char *, GList *, GLoadedContent *); + +/* Envoie un contenu binaire pour conservation côté serveur. */ +bool g_analyst_client_send_content(GAnalystClient *, GBinContent *); + +/* Effectue une demande de sauvegarde de l'état courant. */ +bool g_analyst_client_save(GAnalystClient *); + +/* Ajoute un élément à la collection d'un serveur. */ +bool g_analyst_client_add_item(GAnalystClient *, const GDbItem *); + +/* Active les éléments en amont d'un horodatage donné. */ +bool g_analyst_client_set_last_active(GAnalystClient *, timestamp_t); + +/* Fournit la liste des instantanés existants. */ +bool g_analyst_client_get_snapshots(GAnalystClient *, snapshot_info_t **, size_t *); + +/* Fournit l'identifiant de l'instantané courant. */ +bool g_analyst_client_get_current_snapshot(GAnalystClient *, snapshot_id_t *); + +/* Définit l'identifiant de l'instantané courant. */ +bool g_analyst_client_set_current_snapshot(GAnalystClient *, const snapshot_id_t *); + +/* Définit la désignation d'un instantané donné. */ +bool g_analyst_client_set_snapshot_name(GAnalystClient *, const snapshot_id_t *, const char *); + +/* Définit la description d'un instantané donné. */ +bool g_analyst_client_set_snapshot_desc(GAnalystClient *, const snapshot_id_t *, const char *); + +/* Restaure un ancien instantané. */ +bool g_analyst_client_restore_snapshot(GAnalystClient *, const snapshot_id_t *); + +/* Crée un nouvel instantané à partir d'un autre. */ +bool g_analyst_client_create_snapshot(GAnalystClient *); + +/* Supprime un ancien instantané. */ +bool g_analyst_client_remove_snapshot(GAnalystClient *, const snapshot_id_t *, bool); + + + +#endif /* _ANALYSIS_DB_ANALYST_H */ diff --git a/src/analysis/db/auth.c b/src/analysis/db/auth.c index 5e62f58..5869794 100644 --- a/src/analysis/db/auth.c +++ b/src/analysis/db/auth.c @@ -38,6 +38,7 @@ #include "../../common/io.h" #include "../../common/pathname.h" #include "../../common/xdg.h" +#include "../../common/xml.h" #include "../../core/logs.h" @@ -48,6 +49,14 @@ static char *get_cert_storage_directory(const char *, const char *, const char * /* Calcule l'empreinte d'un fichier de demande de signature. */ static char *compute_csr_fingerprint(const char *); +/* Renvoie un accès à la configuration XML des privilèges. */ +static bool open_server_priv_config(const char *, const char *, xmlDocPtr *, xmlXPathContextPtr *); + +/* Assure la présence d'au moins un administrateur. */ +static bool ensure_one_admin_is_registered(const char *, const char *, const char *); + +/* Enregistre et clôture la configuration XML des privilèges. */ +static bool close_server_priv_config(const char *, const char *, xmlDocPtr, xmlXPathContextPtr); /****************************************************************************** @@ -458,6 +467,8 @@ bool add_client_to_server(const char *host, const char *port, unsigned long vali char *cakey; /* Clef de cette autorité */ char *storage; /* Répertoire de stockage */ char *dest; /* Destination d'une copie */ + x509_entries entries; /* Identitié du client */ + char *id; /* Identifiant associé */ result = false; @@ -517,6 +528,22 @@ bool add_client_to_server(const char *host, const char *port, unsigned long vali } + if (result) + { + result = load_identity_from_cert(hash, &entries); + + if (result) + { + id = translate_x509_entries(&entries); + + result = ensure_one_admin_is_registered(host, port, id); + + free_x509_entries(&entries); + + } + + } + free(storage); } @@ -539,9 +566,132 @@ bool add_client_to_server(const char *host, const char *port, unsigned long vali /****************************************************************************** * * +* Paramètres : host = désignation du serveur à contacter. * +* port = port d'écoute correspondant. * +* xdoc = document XML prêt à emploi. [OUT] * +* context = contexte de recherche XPath. [OUT] * +* * +* Description : Renvoie un accès à la configuration XML des privilèges. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool open_server_priv_config(const char *host, const char *port, xmlDocPtr *xdoc, xmlXPathContextPtr *context) +{ + bool result; /* Bilan à retourner */ + char *filename; /* Chemin d'accès à la config. */ + int ret; /* Test de présence de fichier */ + + filename = get_db_working_directory("servers", host, port, NULL); + filename = stradd(filename, "privs.xml"); + + ret = access(filename, F_OK); + + if (ret == 0) + result = open_xml_file(filename, xdoc, context); + + else + { + result = create_new_xml_file(xdoc, context); + + if (result) + result = (ensure_node_exist(*xdoc, *context, "/ServerPrivLevels") != NULL); + + } + + free(filename); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : host = désignation du serveur à contacter. * +* port = port d'écoute correspondant. * +* id = identification d'un utilisateur. * +* * +* Description : Assure la présence d'au moins un administrateur. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool ensure_one_admin_is_registered(const char *host, const char *port, const char *id) +{ + bool result; /* Bilan à retourner */ + xmlDocPtr xdoc; /* Document XML de configurat° */ + xmlXPathContextPtr context; /* Contexte de recherche XPath */ + xmlXPathObjectPtr xobject; /* Cible d'une recherche */ + size_t count; /* Nombre de contenus premiers */ + + result = open_server_priv_config(host, port, &xdoc, &context); + if (!result) goto exit; + + xobject = get_node_xpath_object(context, "/ServerPrivLevels/Administrators/User"); + + count = XPATH_OBJ_NODES_COUNT(xobject); + + if (count == 0) + result = add_content_to_node(xdoc, context, "/ServerPrivLevels/Administrators/User", id); + + if(xobject != NULL) + xmlXPathFreeObject(xobject); + + result = close_server_priv_config(host, port, xdoc, context); + + exit: + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : host = désignation du serveur à contacter. * +* port = port d'écoute correspondant. * +* xdoc = document XML prêt à emploi. * +* context = contexte de recherche XPath. * +* * +* Description : Enregistre et clôture la configuration XML des privilèges. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool close_server_priv_config(const char *host, const char *port, xmlDocPtr xdoc, xmlXPathContextPtr context) +{ + bool result; /* Bilan à retourner */ + char *filename; /* Chemin d'accès à la config. */ + + filename = get_db_working_directory("servers", host, port, NULL); + filename = stradd(filename, "privs.xml"); + + result = save_xml_file(xdoc, filename); + + close_xml_file(xdoc, context); + + free(filename); + + return result; + +} + + +/****************************************************************************** +* * * Paramètres : - * * * -* Description : Assure la présence d'unenvironnement pour serveur interne. * +* Description : Assure la présence d'un environnement pour serveur interne. * * * * Retour : Bilan de l'opération. * * * diff --git a/src/analysis/db/auth.h b/src/analysis/db/auth.h index 4464244..70b8a9e 100644 --- a/src/analysis/db/auth.h +++ b/src/analysis/db/auth.h @@ -30,7 +30,7 @@ #include "certs.h" - +#include "protocol.h" /* Met en place un canal UNIX pour un serveur interne. */ @@ -51,6 +51,9 @@ bool setup_server_identity(const char *, const char *, unsigned long, x509_entri /* Ajoute un certificat dans les utilisateurs d'un serveur. */ bool add_client_to_server(const char *, const char *, unsigned long, const char *, const char *); + + + /* Assure la présence d'unenvironnement pour serveur interne. */ bool ensure_internal_connections_setup(void); diff --git a/src/analysis/db/backend-int.h b/src/analysis/db/backend-int.h new file mode 100644 index 0000000..c74192c --- /dev/null +++ b/src/analysis/db/backend-int.h @@ -0,0 +1,68 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * backend-int.h - prototypes internes pour le suivi d'une connexion à un serveur + * + * Copyright (C) 2021 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/>. + */ + + +#ifndef _ANALYSIS_DB_BACKEND_INT_H +#define _ANALYSIS_DB_BACKEND_INT_H + + +#include <stdbool.h> + + +#include "backend.h" + + + +/* Prend en compte une connexion nouvelle d'un utilisateur. */ +typedef void (* add_backend_client_fc) (GServerBackend *, SSL *, const char *, const char *); + + +/* Support pour un suivi de connexion à un serveur (instance) */ +struct _GServerBackend +{ + GObject parent; /* A laisser en premier */ + + int stop_ctrl[2]; /* Commande d'arrêt */ + int refresh_ctrl[2]; /* Commande d'actualisation */ + GThread *process; /* Procédure de traitement */ + +}; + +/* Support pour un suivi de connexion à un serveur (classe) */ +struct _GServerBackendClass +{ + GObjectClass parent; /* A laisser en premier */ + + const char *thread_name; /* Désignation de processus */ + GThreadFunc thread_func; /* Traitement des échanges */ + + add_backend_client_fc add_client; /* Intégration d'un client */ + +}; + + +/* Met fin à un support de suivi. */ +void g_server_backend_stop(GServerBackend *); + + + +#endif /* _ANALYSIS_DB_BACKEND_INT_H */ diff --git a/src/analysis/db/backend.c b/src/analysis/db/backend.c new file mode 100644 index 0000000..3a5f46e --- /dev/null +++ b/src/analysis/db/backend.c @@ -0,0 +1,267 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * backend.c - suivi d'une connexion à un serveur + * + * Copyright (C) 2021 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 "backend.h" + + +#include <unistd.h> + + +#include "backend-int.h" +#include "../../core/logs.h" + + + +/* Initialise la classe des supports pour suivi de connexion. */ +static void g_server_backend_class_init(GServerBackendClass *); + +/* Initialise un support pour suivi de connexion. */ +static void g_server_backend_init(GServerBackend *); + +/* Supprime toutes les références externes. */ +static void g_server_backend_dispose(GServerBackend *); + +/* Procède à la libération totale de la mémoire. */ +static void g_server_backend_finalize(GServerBackend *); + + + +/* Indique le type défini pour un Support de suivi de connexion. */ +G_DEFINE_TYPE(GServerBackend, g_server_backend, G_TYPE_OBJECT); + + +/****************************************************************************** +* * +* Paramètres : klass = classe à initialiser. * +* * +* Description : Initialise la classe des supports pour suivi de connexion. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_server_backend_class_init(GServerBackendClass *klass) +{ + GObjectClass *object; /* Autre version de la classe */ + + object = G_OBJECT_CLASS(klass); + + object->dispose = (GObjectFinalizeFunc/* ! */)g_server_backend_dispose; + object->finalize = (GObjectFinalizeFunc)g_server_backend_finalize; + +} + + +/****************************************************************************** +* * +* Paramètres : backend = instance à initialiser. * +* * +* Description : Initialise un support pour suivi de connexion. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_server_backend_init(GServerBackend *backend) +{ + backend->stop_ctrl[0] = -1; + backend->stop_ctrl[1] = -1; + backend->refresh_ctrl[0] = -1; + backend->refresh_ctrl[1] = -1; + backend->process = NULL; + +} + + +/****************************************************************************** +* * +* Paramètres : backend = instance d'objet GLib à traiter. * +* * +* Description : Supprime toutes les références externes. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_server_backend_dispose(GServerBackend *backend) +{ + G_OBJECT_CLASS(g_server_backend_parent_class)->dispose(G_OBJECT(backend)); + +} + + +/****************************************************************************** +* * +* Paramètres : backend = instance d'objet GLib à traiter. * +* * +* Description : Procède à la libération totale de la mémoire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_server_backend_finalize(GServerBackend *backend) +{ + G_OBJECT_CLASS(g_server_backend_parent_class)->finalize(G_OBJECT(backend)); + +} + + +/****************************************************************************** +* * +* Paramètres : backend = support pour le suivi d'une connexion. * +* fd = canal de communication réseau ouvert. * +* peer_name = désignation de la connexion. * +* * +* Description : Prend en compte une connexion nouvelle d'un utilisateur. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void g_server_backend_add_client(GServerBackend *backend, SSL *fd, const char *peer_name) +{ + GServerBackendClass *class; /* Classe du support manipulé */ + X509 *peer_cert; /* Certificat présenté */ + char *user; /* Nom d'utilisateur associé */ + int ret; /* Bilan d'un appel */ + ssize_t sent; /* Quantité de données émises */ + + class = G_SERVER_BACKEND_GET_CLASS(backend); + + /* Ajout dans la liste officielle */ + + peer_cert = SSL_get_peer_certificate(fd); + + user = X509_NAME_oneline(X509_get_subject_name(peer_cert), NULL, -1); + + X509_free(peer_cert); + + class->add_client(backend, fd, peer_name, user); + + free(user); + + /* Démarrage ou redémarrage du processus d'écoute */ + + if (backend->process == NULL) + { + ret = pipe(backend->stop_ctrl); + if (ret != 0) + { + LOG_ERROR_N("pipe"); + g_object_unref(G_OBJECT(backend)); + goto sys_error; + } + + ret = pipe(backend->refresh_ctrl); + if (ret != 0) + { + LOG_ERROR_N("pipe"); + g_object_unref(G_OBJECT(backend)); + goto sys_error; + } + + backend->process = g_thread_new(class->thread_name, class->thread_func, backend); + + sys_error: + + ; + + } + + else + { + sent = write(backend->refresh_ctrl[1], "\xf0", 1); + if (sent != 1) LOG_ERROR_N("write"); + } + +} + + +/****************************************************************************** +* * +* Paramètres : backend = support pour le suivi d'une connexion. * +* * +* Description : Met fin à un support de suivi. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void g_server_backend_stop(GServerBackend *backend) +{ + GThread *process; /* Procédure à terminer */ + int ret; /* Bilan d'un appel */ + ssize_t sent; /* Quantité de données émises */ + + /* Gestion du double appel */ + + if (backend->process == NULL) + return; + + process = backend->process; + + backend->process = NULL; + + /* Ordre d'arrêt */ + + if (g_thread_self() != process) + { + sent = write(backend->stop_ctrl[1], "\xf0", 1); + if (sent != 1) LOG_ERROR_N("write"); + + g_thread_join(process); + + } + + /* Fermeture des flux */ + + ret = close(backend->stop_ctrl[0]); + if (ret == -1) LOG_ERROR_N("close"); + backend->stop_ctrl[0] = -1; + + ret = close(backend->stop_ctrl[1]); + if (ret == -1) LOG_ERROR_N("close"); + backend->stop_ctrl[1] = -1; + + ret = close(backend->refresh_ctrl[0]); + if (ret == -1) LOG_ERROR_N("close"); + backend->refresh_ctrl[0] = -1; + + ret = close(backend->refresh_ctrl[1]); + if (ret == -1) LOG_ERROR_N("close"); + backend->refresh_ctrl[1] = -1; + +} diff --git a/src/analysis/db/backend.h b/src/analysis/db/backend.h new file mode 100644 index 0000000..0aad651 --- /dev/null +++ b/src/analysis/db/backend.h @@ -0,0 +1,56 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * backend.h - prototypes pour le suivi d'une connexion à un serveur + * + * Copyright (C) 2021 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/>. + */ + + +#ifndef _ANALYSIS_DB_BACKEND_H +#define _ANALYSIS_DB_BACKEND_H + + +#include <glib-object.h> +#include <openssl/ssl.h> + + + +#define G_TYPE_SERVER_BACKEND g_server_backend_get_type() +#define G_SERVER_BACKEND(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_SERVER_BACKEND, GServerBackend)) +#define G_IS_SERVER_BACKEND(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_SERVER_BACKEND)) +#define G_SERVER_BACKEND_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_SERVER_BACKEND, GServerBackendClass)) +#define G_IS_SERVER_BACKEND_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_SERVER_BACKEND)) +#define G_SERVER_BACKEND_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_SERVER_BACKEND, GServerBackendClass)) + + +/* Support pour un suivi de connexion à un serveur (instance) */ +typedef struct _GServerBackend GServerBackend; + +/* Support pour un suivi de connexion à un serveur (classe) */ +typedef struct _GServerBackendClass GServerBackendClass; + + +/* Indique le type défini pour un Support de suivi de connexion. */ +GType g_server_backend_get_type(void); + +/* Prend en compte une connexion nouvelle d'un utilisateur. */ +void g_server_backend_add_client(GServerBackend *, SSL *, const char *); + + + +#endif /* _ANALYSIS_DB_BACKEND_H */ diff --git a/src/analysis/db/cdb.c b/src/analysis/db/cdb.c index 9e24f84..6d4b84d 100644 --- a/src/analysis/db/cdb.c +++ b/src/analysis/db/cdb.c @@ -1,6 +1,6 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * cdb.h - prototypes pour la manipulation des archives au format CDB + * cdb.c - manipulation des archives au format CDB * * Copyright (C) 2014-2019 Cyrille Bagard * @@ -26,10 +26,10 @@ #include <assert.h> #include <errno.h> +#include <fcntl.h> #include <malloc.h> #include <poll.h> #include <pthread.h> -#include <signal.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> @@ -38,16 +38,20 @@ #include <sys/stat.h> -#include <i18n.h> #include <config.h> +#include <i18n.h> +#include "backend-int.h" #include "collection.h" #include "protocol.h" #include "snapshot.h" +#include "../content.h" +#include "../storage/storage.h" #include "../../common/compression.h" #include "../../common/cpp.h" #include "../../common/extstr.h" +#include "../../common/io.h" #include "../../common/pathname.h" #include "../../common/xml.h" #include "../../core/collections.h" @@ -55,51 +59,70 @@ -/* ------------------------- COEUR DE LA GESTION D'ARCHIVES ------------------------- */ +/* -------------------------- LIEN VERS UN CLIENT CONNECTE -------------------------- */ /* Informations relatives à un client */ typedef struct _cdb_client { - SSL *ssl_fd; /* Canal de communication */ + SSL *tls_fd; /* Canal de communication */ + char *peer_name; /* Désignation du correspondant*/ char *user; /* Utilisateur à l'autre bout */ - uint64_t last_time; /* Date de dernier envoi */ + gint ref_count; /* Décompte d'utilisation */ } cdb_client; +/* Met en place le suivi d'une connexion de client. */ +static cdb_client *create_cdb_client(SSL *, const char *, const char *); + +/* Supprime le suivi d'une connexion de client. */ +static void delete_cdb_client(cdb_client *); + +/* Augmente le décompte d'utilisation d'un suivi de connexion. */ +static void ref_cdb_client(cdb_client *); + +/* Diminue le décompte d'utilisation d'un suivi de connexion. */ +static void unref_cdb_client(cdb_client *); + + + +/* ------------------------- COEUR DE LA GESTION D'ARCHIVES ------------------------- */ + + /* Description d'une archive d'éléments utilisateur (instance) */ struct _GCdbArchive { - GObject parent; /* A laisser en premier */ + GServerBackend parent; /* A laisser en premier */ rle_string hash; /* Empreinte cryptographique */ + rle_string class; /* Nature du contenu analysé */ char *filename; /* Chemin d'accès à l'archive */ char *tmpdir; /* Répertoire de travail */ char *xml_desc; /* Fichier de description */ + char *cnt_file; /* Fichier de contenu binaire */ + + GMutex loading_access; /* Verrou pour l'accès */ + + GList *collections; /* Ensemble de modifications */ GDbSnapshot *snapshot; /* Instantanés de bases SQL */ sqlite3 *db; /* Base de données à manipuler */ - cdb_client *clients; /* Connexions en place */ + cdb_client **clients; /* Connexions en place */ size_t count; /* Quantité de clients */ GMutex clients_access; /* Verrou pour l'accès */ - GThread *process; /* Procédure de traitement */ - GMutex id_access; /* Accès à l'identifiant */ - GCond id_cond; /* Condition d'attente */ - pthread_t process_id; /* Identifiant de la procédure */ - }; /* Description d'une archive d'éléments utilisateur (classe) */ struct _GCdbArchiveClass { - GObjectClass parent; /* A laisser en premier */ + GServerBackendClass parent; /* A laisser en premier */ }; @@ -147,20 +170,135 @@ static void on_collection_extended(GDbCollection *, GDbItem *, GCdbArchive *); /* Assure le traitement des requêtes de clients. */ static void *g_cdb_archive_process(GCdbArchive *); +/* Prend en compte une connexion nouvelle d'un utilisateur. */ +static void g_cdb_archive_add_client(GCdbArchive *, SSL *, const char *, const char *); + +/* Dissocie un utilisateur de l'archive. */ +static void _g_cdb_archive_remove_client(GCdbArchive *, size_t); + +/* Dissocie un utilisateur de l'archive. */ +static void g_cdb_archive_remove_client(GCdbArchive *, size_t); + /* Envoie un paquet de données constitué à tous les clients. */ -static void g_cdb_archive_send_reply_to_all_clients(GCdbArchive *, packed_buffer *); +static void g_cdb_archive_send_reply_to_all_clients(GCdbArchive *, packed_buffer_t *); /* Envoie à tous les clients la nouvelle liste d'instantanés. */ -static bool g_cdb_archive_send_snapshot_update(GCdbArchive *, packed_buffer *); +static bool g_cdb_archive_send_snapshot_update(GCdbArchive *, packed_buffer_t *); /* Envoie à tous les clients le nouvel instantané courant. */ -static bool g_cdb_archive_send_snapshot_change(GCdbArchive *, packed_buffer *); +static bool g_cdb_archive_send_snapshot_change(GCdbArchive *, packed_buffer_t *); -/* Dissocie un utilisateur de l'archive. */ -static void _g_cdb_archive_remove_client(GCdbArchive *, size_t); -/* Dissocie un utilisateur de l'archive. */ -static void g_cdb_archive_remove_client(GCdbArchive *, size_t); + +/* ------------------------- PRISES EN COMPTE DES COMMANDES ------------------------- */ + + +/* Prépare une courte réponse à envoyer à un client connecté. */ +static bool craft_server_short_answer(DBCommand, uleb128_t, packed_buffer_t *); + +/* Enregistre le contenu binaire lié à une analyse. */ +static bool g_cdb_archive_set_content(GCdbArchive *, packed_buffer_t *, packed_buffer_t *); + + + +/* -------------------------- LIEN VERS UN CLIENT CONNECTE -------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : fd = canal de communication réseau ouvert. * +* peer_name = désignation de la connexion. * +* user = désignation de l'utilisateur de la connexion. * +* * +* Description : Met en place le suivi d'une connexion de client. * +* * +* Retour : Structure dédiée construite. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static cdb_client *create_cdb_client(SSL *fd, const char *peer_name, const char *user) +{ + cdb_client *result; /* Fiche d'entité à retourner */ + + result = malloc(sizeof(cdb_client)); + + result->tls_fd = fd; + + result->peer_name = strdup(peer_name); + result->user = strdup(user); + + g_atomic_int_set(&result->ref_count, 1); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : client = informations de suivi à libérer de la mémoire. * +* * +* Description : Supprime le suivi d'une connexion de client. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void delete_cdb_client(cdb_client *client) +{ + assert(g_atomic_int_get(&client->ref_count) == 0); + + SSL_free(client->tls_fd); + + free(client->peer_name); + free(client->user); + + free(client); + +} + + +/****************************************************************************** +* * +* Paramètres : client = informations de suivi à libérer de la mémoire. * +* * +* Description : Augmente le décompte d'utilisation d'un suivi de connexion. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void ref_cdb_client(cdb_client *client) +{ + g_atomic_int_inc(&client->ref_count); + +} + + +/****************************************************************************** +* * +* Paramètres : client = informations de suivi à libérer de la mémoire. * +* * +* Description : Diminue le décompte d'utilisation d'un suivi de connexion. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void unref_cdb_client(cdb_client *client) +{ + if (g_atomic_int_dec_and_test(&client->ref_count)) + delete_cdb_client(client); + +} @@ -170,7 +308,7 @@ static void g_cdb_archive_remove_client(GCdbArchive *, size_t); /* Indique le type défini pour une une archive d'éléments utilisateur. */ -G_DEFINE_TYPE(GCdbArchive, g_cdb_archive, G_TYPE_OBJECT); +G_DEFINE_TYPE(GCdbArchive, g_cdb_archive, G_TYPE_SERVER_BACKEND); /****************************************************************************** @@ -188,12 +326,20 @@ G_DEFINE_TYPE(GCdbArchive, g_cdb_archive, G_TYPE_OBJECT); static void g_cdb_archive_class_init(GCdbArchiveClass *klass) { GObjectClass *object; /* Autre version de la classe */ + GServerBackendClass *backend; /* Classe parente */ object = G_OBJECT_CLASS(klass); object->dispose = (GObjectFinalizeFunc/* ! */)g_cdb_archive_dispose; object->finalize = (GObjectFinalizeFunc)g_cdb_archive_finalize; + backend = G_SERVER_BACKEND_CLASS(klass); + + backend->thread_name = "cdb_archiver"; + backend->thread_func = (GThreadFunc)g_cdb_archive_process; + + backend->add_client = (add_backend_client_fc)g_cdb_archive_add_client; + } @@ -212,18 +358,22 @@ static void g_cdb_archive_class_init(GCdbArchiveClass *klass) static void g_cdb_archive_init(GCdbArchive *archive) { setup_empty_rle_string(&archive->hash); + setup_empty_rle_string(&archive->class); + archive->filename = NULL; archive->tmpdir = NULL; + archive->xml_desc = NULL; + + archive->cnt_file = NULL; + g_mutex_init(&archive->loading_access); archive->collections = create_collections_list(); archive->snapshot = NULL; + archive->db = NULL; g_mutex_init(&archive->clients_access); - g_mutex_init(&archive->id_access); - g_cond_init(&archive->id_cond); - } @@ -241,13 +391,14 @@ static void g_cdb_archive_init(GCdbArchive *archive) static void g_cdb_archive_dispose(GCdbArchive *archive) { - g_clear_object(&archive->snapshot); - - g_cond_clear(&archive->id_cond); - g_mutex_clear(&archive->id_access); + g_server_backend_stop(G_SERVER_BACKEND(archive)); g_mutex_clear(&archive->clients_access); + g_clear_object(&archive->snapshot); + + g_mutex_clear(&archive->loading_access); + G_OBJECT_CLASS(g_cdb_archive_parent_class)->dispose(G_OBJECT(archive)); } @@ -281,14 +432,19 @@ static void g_cdb_archive_finalize(GCdbArchive *archive) #endif } + if (archive->cnt_file != NULL) + free(archive->cnt_file); + if (archive->xml_desc != NULL) free(archive->xml_desc); - free(archive->filename); - if (archive->tmpdir != NULL) free(archive->tmpdir); + if (archive->filename != NULL) + free(archive->filename); + + exit_rle_string(&archive->class); exit_rle_string(&archive->hash); G_OBJECT_CLASS(g_cdb_archive_parent_class)->finalize(G_OBJECT(archive)); @@ -301,6 +457,7 @@ static void g_cdb_archive_finalize(GCdbArchive *archive) * Paramètres : basedir = répertoire de stockage des enregistrements. * * tmpdir = répertoire de travail temporaire. * * hash = empreinte du binaire à représenter. * +* class = nature du contenu analysé associé. * * error = indication éventuelle en cas d'échec. [OUT] * * * * Description : Définit ou ouvre une archive d'éléments utilisateur. * @@ -312,7 +469,7 @@ static void g_cdb_archive_finalize(GCdbArchive *archive) * * ******************************************************************************/ -GCdbArchive *g_cdb_archive_new(const char *basedir, const char *tmpdir, const rle_string *hash, DBError *error) +GCdbArchive *g_cdb_archive_new(const char *basedir, const char *tmpdir, const rle_string *hash, const rle_string *class, DBError *error) { GCdbArchive *result; /* Adresse à retourner */ int ret; /* Retour d'un appel */ @@ -321,13 +478,16 @@ GCdbArchive *g_cdb_archive_new(const char *basedir, const char *tmpdir, const rl result = g_object_new(G_TYPE_CDB_ARCHIVE, NULL); dup_into_rle_string(&result->hash, get_rle_string(hash)); + dup_into_rle_string(&result->class, get_rle_string(class)); *error = DBE_SYS_ERROR; /* Chemin de l'archive */ result->filename = strdup(basedir); - result->filename = stradd(result->filename, hash->data); + result->filename = stradd(result->filename, get_rle_string(hash)); + result->filename = stradd(result->filename, "-"); + result->filename = stradd(result->filename, get_rle_string(class)); result->filename = stradd(result->filename, ".cdb.tar.xz"); if (!mkpath(result->filename)) @@ -340,11 +500,8 @@ GCdbArchive *g_cdb_archive_new(const char *basedir, const char *tmpdir, const rl if (!mkpath(tmpdir)) goto error; - ret = asprintf(&result->xml_desc, "%s" G_DIR_SEPARATOR_S "%s_desc.xml", tmpdir, get_rle_string(hash)); - if (ret == -1) goto no_tmp; - - ret = ensure_path_exists(result->xml_desc); - if (ret == -1) goto no_tmp; + result->xml_desc = g_cdb_archive_get_tmp_filename(result, "desc.xml"); + if (result->xml_desc == NULL) goto no_tmp; /* Création de l'archive si elle n'existe pas */ @@ -355,7 +512,7 @@ GCdbArchive *g_cdb_archive_new(const char *basedir, const char *tmpdir, const rl /* Le soucis ne vient pas de l'absence du fichier... */ if (errno != ENOENT) goto error; - result->snapshot = g_db_snapshot_new_empty(tmpdir, get_rle_string(hash), result->collections); + result->snapshot = g_db_snapshot_new_empty(result, result->collections); if (result->snapshot == NULL) goto error; @@ -426,6 +583,47 @@ GCdbArchive *g_cdb_archive_new(const char *basedir, const char *tmpdir, const rl /****************************************************************************** * * * Paramètres : archive = informations quant à l'archive à interpréter. * +* suffix = fin du nom de fichier à définir. * +* * +* Description : Construit un chemin pour un fichier propre à l'archive. * +* * +* Retour : Chemin de fichier à utiliser ou NULL en cas d'erreur. * +* * +* Remarques : - * +* * +******************************************************************************/ + +char *g_cdb_archive_get_tmp_filename(const GCdbArchive *archive, const char *suffix) +{ + char *result; /* Chemin à retourner */ + int ret; /* Retour d'un appel */ + + ret = asprintf(&result, "%s" G_DIR_SEPARATOR_S "%s_%s", + archive->tmpdir, get_rle_string(&archive->hash), suffix); + + if (ret == -1) + result = NULL; + + else + { + ret = ensure_path_exists(result); + + if (ret == -1) + { + free(result); + result = NULL; + } + + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : archive = informations quant à l'archive à interpréter. * * * * Description : Ouvre une archive avec tous les éléments à conserver. * * * @@ -475,8 +673,7 @@ static DBError g_cdb_archive_read(GCdbArchive *archive) goto load_error; } - archive->snapshot = g_db_snapshot_new_from_xml(archive->tmpdir, get_rle_string(&archive->hash), - xdoc, context); + archive->snapshot = g_db_snapshot_new_from_xml(archive, xdoc, context); close_xml_file(xdoc, context); @@ -503,7 +700,7 @@ static DBError g_cdb_archive_read(GCdbArchive *archive) ret = archive_read_open_filename(in, archive->filename, 10240 /* ?! */); if (ret != ARCHIVE_OK) goto bad_archive; - status = g_db_snapshot_fill(archive->snapshot, in); + status = g_db_snapshot_fill(archive->snapshot, in, archive); if (!status) goto load_error; result = DBE_NONE; @@ -616,18 +813,26 @@ DBError g_cdb_archive_write(const GCdbArchive *archive) * * * Paramètres : archive = informations quant à l'archive à consulter. * * hash = empreinte extérieure à comparer. * +* class = nature du contenu analysé. * * * -* Description : Détermine si une empreinte correspond à celle d'une archive. * +* Description : Détermine l'archive correspond à une cible recherchée. * * * -* Retour : Résultat de la comparaison : -1, 0 ou 1. * +* Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ -int g_cdb_archive_compare_hash(const GCdbArchive *archive, const rle_string *hash) +bool g_cdb_archive_compare_is_suitable_for(const GCdbArchive *archive, const rle_string *hash, const rle_string *class) { - return cmp_rle_string(&archive->hash, hash); + bool result; /* Bilan à retourner */ + + result = (cmp_rle_string(&archive->hash, hash) == 0); + + if (result) + result = (cmp_rle_string(&archive->class, class) == 0); + + return result; } @@ -792,7 +997,7 @@ static void g_cdb_archive_register_signals(GCdbArchive *archive) static void on_collection_extended(GDbCollection *collec, GDbItem *item, GCdbArchive *archive) { - packed_buffer pbuf; /* Tampon d'émission */ + packed_buffer_t pbuf; /* Tampon d'émission */ size_t i; /* Boucle de parcours */ bool status; /* Bilan d'un envoi de retour */ @@ -804,7 +1009,7 @@ static void on_collection_extended(GDbCollection *collec, GDbItem *item, GCdbArc for (i = 0; i < archive->count && status; i++) { - status = ssl_send_packed_buffer(&pbuf, archive->clients[i].ssl_fd); + status = ssl_send_packed_buffer(&pbuf, archive->clients[i]->tls_fd); if (!status) LOG_ERROR(LMT_ERROR, _("Failed to send some DB update")); @@ -832,66 +1037,91 @@ static void on_collection_extended(GDbCollection *collec, GDbItem *item, GCdbArc static void *g_cdb_archive_process(GCdbArchive *archive) { + GServerBackend *base; /* Base de l'instance */ + cdb_client **clients; /* Clients surveillés */ + size_t last_count; /* Quantité de ces clients */ struct pollfd *fds; /* Surveillance des flux */ nfds_t nfds; /* Quantité de ces flux */ nfds_t i; /* Boucle de parcours */ int ret; /* Bilan d'un appel */ - packed_buffer in_pbuf; /* Tampon de réception */ + packed_buffer_t in_pbuf; /* Tampon de réception */ uint32_t tmp32; /* Valeur sur 32 bits */ bool status; /* Bilan de lecture initiale */ uint32_t command; /* Commande de la requête */ DBError error; /* Bilan d'une opération */ - packed_buffer out_pbuf; /* Tampon d'émission */ + packed_buffer_t out_pbuf; /* Tampon d'émission */ GDbCollection *collec; /* Collection visée au final */ bool reload; /* Besoin de rechargement */ char *msg; /* Erreur à faire remonter */ - void interrupt_poll_with_sigusr1(int sig) { }; + base = G_SERVER_BACKEND(archive); - signal(SIGUSR1, interrupt_poll_with_sigusr1); - - g_mutex_lock(&archive->id_access); - archive->process_id = pthread_self(); - g_cond_signal(&archive->id_cond); - g_mutex_unlock(&archive->id_access); + clients = NULL; + last_count = 0; fds = NULL; while (1) { + /* Réinitialisation ? */ + + for (i = 0; i < last_count; i++) + unref_cdb_client(clients[i]); + /* Reconstitution d'une liste à jour */ g_mutex_lock(&archive->clients_access); - nfds = archive->count; + last_count = archive->count; + + clients = realloc(clients, last_count * sizeof(cdb_client)); + + for (i = 0; i < last_count; i++) + { + clients[i] = archive->clients[i]; + ref_cdb_client(clients[i]); + } + + nfds = last_count + 2; fds = realloc(fds, nfds * sizeof(struct pollfd)); - for (i = 0; i < nfds; i++) + for (i = 0; i < (nfds - 2); i++) { - fds[i].fd = SSL_get_fd(archive->clients[i].ssl_fd); + fds[i].fd = SSL_get_fd(clients[i]->tls_fd); fds[i].events = POLLIN | POLLPRI; } g_mutex_unlock(&archive->clients_access); - if (nfds == 0) + if (nfds == 2) goto gcap_no_more_clients; + fds[nfds - 2].fd = base->stop_ctrl[0]; + fds[nfds - 2].events = POLLIN | POLLPRI; + + fds[nfds - 1].fd = base->refresh_ctrl[0]; + fds[nfds - 1].events = POLLIN | POLLPRI; + /* Lancement d'une phase de surveillance */ ret = poll(fds, nfds, -1); if (ret == -1) { - if (errno == EINTR) continue; - LOG_ERROR_N("poll"); break; - } + /* Demande expresse d'arrêt des procédures */ + if (fds[nfds - 2].revents) + break; + + /* Demande d'actualisation */ + if (fds[nfds - 1].revents) + continue; + /* Traitement des requêtes reçues */ - for (i = 0; i < nfds; i++) + for (i = 0; i < (nfds - 1); i++) { /* Le canal est fermé, une sortie doit être demandée... */ if (fds[i].revents & POLLNVAL) @@ -910,7 +1140,7 @@ static void *g_cdb_archive_process(GCdbArchive *archive) { init_packed_buffer(&in_pbuf); - status = ssl_recv_packed_buffer(&in_pbuf, archive->clients[i].ssl_fd); + status = ssl_recv_packed_buffer(&in_pbuf, clients[i]->tls_fd); if (!status) goto gcap_bad_exchange; next_command: @@ -920,13 +1150,21 @@ static void *g_cdb_archive_process(GCdbArchive *archive) command = tmp32; + init_packed_buffer(&out_pbuf); + switch (command) { + case DBC_SET_CONTENT: + status = g_cdb_archive_set_content(archive, &in_pbuf, &out_pbuf); + break; + + + case DBC_SAVE: error = g_cdb_archive_write(archive); - init_packed_buffer(&out_pbuf); + //init_packed_buffer(&out_pbuf); status = extend_packed_buffer(&out_pbuf, (uint32_t []) { DBC_SAVE }, sizeof(uint32_t), true); @@ -935,7 +1173,7 @@ static void *g_cdb_archive_process(GCdbArchive *archive) status = extend_packed_buffer(&out_pbuf, (uint32_t []) { error }, sizeof(uint32_t), true); if (!status) goto gcap_bad_reply; - status = ssl_send_packed_buffer(&out_pbuf, archive->clients[i].ssl_fd); + status = ssl_send_packed_buffer(&out_pbuf, clients[i]->tls_fd); if (!status) goto gcap_bad_reply; exit_packed_buffer(&out_pbuf); @@ -976,10 +1214,10 @@ static void *g_cdb_archive_process(GCdbArchive *archive) status = extend_packed_buffer(&out_pbuf, (uint8_t []) { 0x0 }, sizeof(uint8_t), true); if (!status) goto gcap_bad_reply; - status = ssl_send_packed_buffer(&out_pbuf, archive->clients[i].ssl_fd); - if (!status) goto gcap_bad_reply; + //status = ssl_send_packed_buffer(&out_pbuf, clients[i]->tls_fd); + //if (!status) goto gcap_bad_reply; - exit_packed_buffer(&out_pbuf); + //exit_packed_buffer(&out_pbuf); break; @@ -998,7 +1236,7 @@ static void *g_cdb_archive_process(GCdbArchive *archive) &in_pbuf, &out_pbuf, archive->db); if (!status) goto gcap_bad_reply; - status = ssl_send_packed_buffer(&out_pbuf, archive->clients[i].ssl_fd); + status = ssl_send_packed_buffer(&out_pbuf, clients[i]->tls_fd); if (!status) goto gcap_bad_reply; exit_packed_buffer(&out_pbuf); @@ -1010,10 +1248,12 @@ static void *g_cdb_archive_process(GCdbArchive *archive) if (!g_cdb_archive_send_snapshot_update(archive, &out_pbuf)) goto critical_error; - status = ssl_send_packed_buffer(&out_pbuf, archive->clients[i].ssl_fd); - if (!status) goto gcap_bad_reply; + status = true; - exit_packed_buffer(&out_pbuf); + //status = ssl_send_packed_buffer(&out_pbuf, clients[i]->tls_fd); + //if (!status) goto gcap_bad_reply; + + //exit_packed_buffer(&out_pbuf); break; @@ -1022,10 +1262,10 @@ static void *g_cdb_archive_process(GCdbArchive *archive) if (!g_cdb_archive_send_snapshot_change(archive, &out_pbuf)) goto critical_error; - status = ssl_send_packed_buffer(&out_pbuf, archive->clients[i].ssl_fd); - if (!status) goto gcap_bad_reply; + //status = ssl_send_packed_buffer(&out_pbuf, clients[i]->tls_fd); + //if (!status) goto gcap_bad_reply; - exit_packed_buffer(&out_pbuf); + //exit_packed_buffer(&out_pbuf); break; @@ -1100,7 +1340,7 @@ static void *g_cdb_archive_process(GCdbArchive *archive) case DBC_CREATE_SNAPSHOT: - error = g_db_snapshot_create(archive->snapshot, archive->db); + error = g_db_snapshot_create(archive->snapshot, archive->db, archive); if (error == DBE_NONE) { @@ -1144,6 +1384,17 @@ static void *g_cdb_archive_process(GCdbArchive *archive) } + if (!status) + goto gcap_bad_reply; + + if (get_packed_buffer_payload_length(&out_pbuf) > 0) + { + status = ssl_send_packed_buffer(&out_pbuf, clients[i]->tls_fd); + if (!status) goto gcap_bad_reply; + } + + exit_packed_buffer(&out_pbuf); + if (has_more_data_in_packed_buffer(&in_pbuf)) goto next_command; @@ -1185,12 +1436,13 @@ static void *g_cdb_archive_process(GCdbArchive *archive) gcap_no_more_clients: - archive->process = NULL; + g_server_backend_stop(G_SERVER_BACKEND(archive)); + + for (i = 0; i < last_count; i++) + unref_cdb_client(clients[i]); - g_mutex_lock(&archive->id_access); - archive->process_id = 0; - g_cond_signal(&archive->id_cond); - g_mutex_unlock(&archive->id_access); + if (clients != NULL) + free(clients); if (fds != NULL) free(fds); @@ -1202,6 +1454,170 @@ static void *g_cdb_archive_process(GCdbArchive *archive) /****************************************************************************** * * +* Paramètres : archive = support pour le suivi d'une connexion. * +* fd = canal de communication réseau ouvert. * +* peer_name = désignation de la connexion. * +* user = désignation de l'utilisateur de la connexion. * +* * +* Description : Prend en compte une connexion nouvelle d'un utilisateur. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static LoadingStatusHint g_cdb_archive_compute_loading_hint(GCdbArchive *archive) +{ + LoadingStatusHint result; /* Statut à retourner */ + + + // Try + // g_mutex_lock(&archive->loading_access); + + + + // cnt_file + + if (archive->cnt_file == NULL) + result = LSH_NEED_CONTENT; + + else + result = LSH_NEED_FORMAT; + + + + + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : archive = support pour le suivi d'une connexion. * +* fd = canal de communication réseau ouvert. * +* peer_name = désignation de la connexion. * +* user = désignation de l'utilisateur de la connexion. * +* * +* Description : Prend en compte une connexion nouvelle d'un utilisateur. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_cdb_archive_add_client(GCdbArchive *archive, SSL *fd, const char *peer_name, const char *user) +{ + cdb_client *client; /* Nouvelle fiche d'entité */ + packed_buffer_t out_pbuf; /* Tampon d'émission */ + LoadingStatusHint hint; /* Statut de chargement */ + bool status; /* Bilan de lecture initiale */ + + client = create_cdb_client(fd, peer_name, user); + + /** + * Le verrou encadrant les évolutions des contenus initiaux doit englober + * l'extension de la liste des clients. + * + * En effet, une évolution partielle peut intervenir dans la fonction + * g_cdb_archive_process(), à un moment au seul le verrou dans les + * évolutions sera posé (g_cdb_archive_set_content() par exemple). + * + * Or g_cdb_archive_compute_loading_hint() doit fournir ici un état qui ne + * varie pas entre le calcul et l'envoi. Donc verrous sur les clients et + * l'état de l'archive doivent englover l'ensemble des traitements ci-après. + */ + + g_mutex_lock(&archive->loading_access); + + g_mutex_lock(&archive->clients_access); + + hint = g_cdb_archive_compute_loading_hint(archive); + + if (hint != LSH_READY) + hint = (archive->count == 0 ? hint : LSH_ON_WAIT_LIST); + + init_packed_buffer(&out_pbuf); + + status = craft_server_short_answer(DBC_LOADING_STATUS, hint, &out_pbuf); + + if (status) + status = ssl_send_packed_buffer(&out_pbuf, fd); + + exit_packed_buffer(&out_pbuf); + + if (status) + { + archive->clients = realloc(archive->clients, ++archive->count * sizeof(cdb_client *)); + + archive->clients[archive->count - 1] = client; + + } + + g_mutex_unlock(&archive->clients_access); + + g_mutex_unlock(&archive->loading_access); + +} + + +/****************************************************************************** +* * +* Paramètres : archive = archive à connecter avec un utilisateur. * +* index = indice de l'utilisateur concerné. * +* * +* Description : Dissocie un utilisateur de l'archive. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void _g_cdb_archive_remove_client(GCdbArchive *archive, size_t index) +{ + assert(!g_mutex_trylock(&archive->clients_access)); + + unref_cdb_client(archive->clients[index]); + + if ((index + 1) < archive->count) + memmove(&archive->clients[index], &archive->clients[index + 1], + (archive->count - index - 1) * sizeof(cdb_client *)); + + archive->clients = realloc(archive->clients, --archive->count * sizeof(cdb_client *)); + +} + + +/****************************************************************************** +* * +* Paramètres : archive = archive à connecter avec un utilisateur. * +* index = indice de l'utilisateur concerné. * +* * +* Description : Dissocie un utilisateur de l'archive. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_cdb_archive_remove_client(GCdbArchive *archive, size_t index) +{ + g_mutex_lock(&archive->clients_access); + + _g_cdb_archive_remove_client(archive, index); + + g_mutex_unlock(&archive->clients_access); + +} + + +/****************************************************************************** +* * * Paramètres : archive = archive à connecter avec un utilisateur. * * pbuf = paquet de données à émettre. * * * @@ -1213,7 +1629,7 @@ static void *g_cdb_archive_process(GCdbArchive *archive) * * ******************************************************************************/ -static void g_cdb_archive_send_reply_to_all_clients(GCdbArchive *archive, packed_buffer *pbuf) +static void g_cdb_archive_send_reply_to_all_clients(GCdbArchive *archive, packed_buffer_t *pbuf) { size_t i; /* Boucle de parcours */ bool status; /* Bilan d'une émission */ @@ -1222,7 +1638,7 @@ static void g_cdb_archive_send_reply_to_all_clients(GCdbArchive *archive, packed for (i = 0; i < archive->count; i++) { - status = ssl_send_packed_buffer(pbuf, archive->clients[i].ssl_fd); + status = ssl_send_packed_buffer(pbuf, archive->clients[i]->tls_fd); if (!status) { log_variadic_message(LMT_ERROR, _("Error while replying to client %zu"), i); @@ -1252,11 +1668,11 @@ static void g_cdb_archive_send_reply_to_all_clients(GCdbArchive *archive, packed * * ******************************************************************************/ -static bool g_cdb_archive_send_snapshot_update(GCdbArchive *archive, packed_buffer *pbuf) +static bool g_cdb_archive_send_snapshot_update(GCdbArchive *archive, packed_buffer_t *pbuf) { bool result; /* Bilan à retourner */ bool do_send; /* Réalisation de l'émission */ - packed_buffer out_pbuf; /* Tampon d'émission */ + packed_buffer_t out_pbuf; /* Tampon d'émission */ do_send = (pbuf == NULL); @@ -1301,11 +1717,11 @@ static bool g_cdb_archive_send_snapshot_update(GCdbArchive *archive, packed_buff * * ******************************************************************************/ -static bool g_cdb_archive_send_snapshot_change(GCdbArchive *archive, packed_buffer *pbuf) +static bool g_cdb_archive_send_snapshot_change(GCdbArchive *archive, packed_buffer_t *pbuf) { bool result; /* Bilan à retourner */ bool do_send; /* Réalisation de l'émission */ - packed_buffer out_pbuf; /* Tampon d'émission */ + packed_buffer_t out_pbuf; /* Tampon d'émission */ snapshot_id_t id; /* Identifiant d'instantané */ do_send = (pbuf == NULL); @@ -1339,132 +1755,186 @@ static bool g_cdb_archive_send_snapshot_change(GCdbArchive *archive, packed_buff } + +/* ---------------------------------------------------------------------------------- */ +/* PRISES EN COMPTE DES COMMANDES */ +/* ---------------------------------------------------------------------------------- */ + + /****************************************************************************** * * -* Paramètres : archive = archive à connecter avec un utilisateur. * -* fd = canal de communication réseau ouvert. * +* Paramètres : cmd = commande à l'origine d'un traitement. * +* value = valeur à communiquer. * +* out_pbuf = paquet à consituer pour un retour au client. [OUT]* * * -* Description : Associe un nouvel utilisateur à l'archive. * +* Description : Prépare une courte réponse à envoyer à un client connecté. * * * -* Retour : - * +* Retour : Indication pour le maintien de la communication. * * * * Remarques : - * * * ******************************************************************************/ -void g_cdb_archive_add_client(GCdbArchive *archive, SSL *fd) +static bool craft_server_short_answer(DBCommand cmd, uleb128_t value, packed_buffer_t *out_pbuf) { - X509 *peer_cert; /* Certificat présenté */ - volatile pthread_t *process_id; /* Identifiant de la procédure */ + bool result; /* Bilan à retourner */ - /** - * La situation est un peu compliquée lors de l'accueil d'un nouveau client : - * - * - soit on envoie tous les éléments à prendre en compte, mais on doit - * bloquer la base de données jusqu'à l'intégration pleine et entière - * du client, afin que ce dernier ne loupe pas l'envoi d'un nouvel - * élément entre temps. - * - * - soit on intègre le client et celui ci demande des mises à jour - * collection par collection ; c'est également à lui qui revient le rejet - * des éléments envoyés en solitaires avant la réception de la base - * complète. - * - * On fait le choix du second scenario ici, du fait de la difficulté - * de maîtriser facilement la reconstitution d'une liste de clients dans - * g_cdb_archive_process() depuis un autre flot d'exécution. - */ + init_packed_buffer(out_pbuf); - /* Ajout dans la liste officielle */ + result = extend_packed_buffer(out_pbuf, (uint32_t []) { cmd }, sizeof(uint32_t), true); - g_mutex_lock(&archive->clients_access); + if (result) + result = pack_uleb128((uleb128_t []){ value }, out_pbuf); - archive->clients = realloc(archive->clients, ++archive->count * sizeof(cdb_client)); + return result; - archive->clients[archive->count - 1].ssl_fd = fd; +} - peer_cert = SSL_get_peer_certificate(fd); - archive->clients[archive->count - 1].user = X509_NAME_oneline(X509_get_subject_name(peer_cert), NULL, -1); +/****************************************************************************** +* * +* Paramètres : archive = archive à connecter avec un utilisateur. * +* in_pbuf = paquet à consulter. * +* out_pbuf = paquet à consituer pour un retour au client. [OUT]* +* * +* Description : Enregistre le contenu binaire lié à une analyse. * +* * +* Retour : Indication pour le maintien de la communication. * +* * +* Remarques : - * +* * +******************************************************************************/ - X509_free(peer_cert); +static bool g_cdb_archive_set_content(GCdbArchive *archive, packed_buffer_t *in_pbuf, packed_buffer_t *out_pbuf) +{ + bool result; /* Bilan à retourner */ + DBError error; /* Bilan d'une opération */ + uleb128_t data_length; /* Taille du contenu stocké */ + void *data; /* Données du stockage */ + packed_buffer_t test_pbuf; /* Copie des données pour test */ + uleb128_t pos; /* Position du contenu */ + GObjectStorage *storage; /* Gestionnaire de stockage */ + GSerializableObject *content; /* Contenu restitué */ + const gchar *hash; /* Empreinte de ce contenu */ + int fd; /* Flux ouvert en écriture */ + bool status; /* Bilan d'une écriture */ + LoadingStatusHint hint; /* Statut de chargement */ - /* Démarrage ou redémarrage du processus d'écoute */ + result = true; + error = DBE_NONE; - if (archive->process == NULL) - { - archive->process = g_thread_new("cdb_process", (GThreadFunc)g_cdb_archive_process, archive); + /* Récupération de la charge utile */ + + result = unpack_uleb128(&data_length, in_pbuf); + if (!result) goto exit; + + data = malloc(data_length); + + result = extract_packed_buffer(in_pbuf, data, data_length, false); + if (!result) goto free_and_exit; + + /* Validation de l'empreinte du contenu */ + + init_packed_buffer(&test_pbuf); - /* On attend que le processus parallèle soit prêt */ + result = extend_packed_buffer(&test_pbuf, data, data_length, false); + if (!result) goto check_failure; - process_id = &archive->process_id; + rewind_packed_buffer(&test_pbuf); - g_mutex_lock(&archive->id_access); - while (process_id == 0) - g_cond_wait(&archive->id_cond, &archive->id_access); - g_mutex_unlock(&archive->id_access); + result = unpack_uleb128(&pos, &test_pbuf); + if (!result) goto check_failure; + storage = g_object_storage_load(&test_pbuf); + if (storage == NULL) + { + result = false; + goto check_failure; } - else - pthread_kill(archive->process_id, SIGUSR1); - g_mutex_unlock(&archive->clients_access); + content = g_object_storage_load_object(storage, "contents", pos); + if (!G_IS_BIN_CONTENT(content)) + { + result = false; + goto storage_check_failure; + } -} + hash = g_binary_content_get_checksum(G_BIN_CONTENT(content)); + if (strcmp(hash, get_rle_string(&archive->hash)) != 0) + error = DBE_WRONG_HASH; -/****************************************************************************** -* * -* Paramètres : archive = archive à connecter avec un utilisateur. * -* index = indice de l'utilisateur concerné. * -* * -* Description : Dissocie un utilisateur de l'archive. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ + g_object_unref(G_OBJECT(content)); -static void _g_cdb_archive_remove_client(GCdbArchive *archive, size_t index) -{ - cdb_client *client; /* Client à traiter */ + storage_check_failure: - assert(!g_mutex_trylock(&archive->clients_access)); + g_object_unref(G_OBJECT(storage)); - client = &archive->clients[index]; + check_failure: - SSL_free(client->ssl_fd); - free(client->user); + exit_packed_buffer(&test_pbuf); - if ((index + 1) < archive->count) - memmove(&archive->clients[index], &archive->clients[index + 1], - (archive->count - index - 1) * sizeof(cdb_client)); + if (!result) goto free_and_exit; - archive->clients = realloc(archive->clients, --archive->count * sizeof(cdb_client)); + /* Enregistrement sur disque */ -} + if (error == DBE_NONE) + { + if (archive->cnt_file != NULL) + free(archive->cnt_file); + archive->cnt_file = g_cdb_archive_get_tmp_filename(archive, "storedcontent.bin"); + if (archive->cnt_file == NULL) + { + error = DBE_SYS_ERROR; + goto save_error; + } -/****************************************************************************** -* * -* Paramètres : archive = archive à connecter avec un utilisateur. * -* index = indice de l'utilisateur concerné. * -* * -* Description : Dissocie un utilisateur de l'archive. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ + fd = open(archive->cnt_file, O_WRONLY | O_CREAT, 0600); + if (fd == -1) + { + error = DBE_SYS_ERROR; + goto save_error; + } -static void g_cdb_archive_remove_client(GCdbArchive *archive, size_t index) -{ - g_mutex_lock(&archive->clients_access); + status = safe_write(fd, data, data_length); - _g_cdb_archive_remove_client(archive, index); + if (!status) + { + unlink(archive->cnt_file); + free(archive->cnt_file); + archive->cnt_file = NULL; - g_mutex_unlock(&archive->clients_access); + error = DBE_SYS_ERROR; + + } + + close(fd); + + save_error: + + ; + + } + + /* Formulation de la réponse */ + + result = craft_server_short_answer(DBC_SET_CONTENT, error, out_pbuf); + + if (result && error == DBE_NONE) + { + hint = g_cdb_archive_compute_loading_hint(archive); + + result = craft_server_short_answer(DBC_LOADING_STATUS, hint, out_pbuf); + + } + + free_and_exit: + + free(data); + + exit: + + return result; } diff --git a/src/analysis/db/cdb.h b/src/analysis/db/cdb.h index 58bb781..7a557f2 100644 --- a/src/analysis/db/cdb.h +++ b/src/analysis/db/cdb.h @@ -54,22 +54,16 @@ typedef struct _GCdbArchiveClass GCdbArchiveClass; GType g_cdb_archive_get_type(void); /* Prépare un client pour une connexion à une BD. */ -GCdbArchive *g_cdb_archive_new(const char *, const char *, const rle_string *, DBError *); +GCdbArchive *g_cdb_archive_new(const char *, const char *, const rle_string *, const rle_string *, DBError *); + +/* Construit un chemin pour un fichier propre à l'archive. */ +char *g_cdb_archive_get_tmp_filename(const GCdbArchive *, const char *); /* Enregistre une archive avec tous les éléments à conserver. */ DBError g_cdb_archive_write(const GCdbArchive *); -/* Détermine si une empreinte correspond à celle d'une archive. */ -int g_cdb_archive_compare_hash(const GCdbArchive *, const rle_string *); - - - - - -/* Associe un nouvel utilisateur à l'archive. */ -void g_cdb_archive_add_client(GCdbArchive *, SSL *); - - +/* Détermine l'archive correspond à une cible recherchée. */ +bool g_cdb_archive_compare_is_suitable_for(const GCdbArchive *, const rle_string *, const rle_string *); diff --git a/src/analysis/db/certs.c b/src/analysis/db/certs.c index dabc127..22ff397 100644 --- a/src/analysis/db/certs.c +++ b/src/analysis/db/certs.c @@ -39,6 +39,7 @@ #include <i18n.h> +#include "../../common/extstr.h" #include "../../core/logs.h" @@ -52,6 +53,9 @@ static bool add_extension_to_req(STACK_OF(X509_EXTENSION) *, int, /*const */char /* Crée une paire de clefs RSA. */ static RSA *generate_rsa_key(unsigned int, unsigned long); +/* Recharge l'identité inscrite dans un élément X509. */ +static bool load_identity_from_x509(/*const */X509_NAME *, x509_entries *); + /****************************************************************************** @@ -97,6 +101,70 @@ bool are_x509_entries_empty(const x509_entries *entries) /****************************************************************************** * * +* Paramètres : entries = éléments d'identité à convertir. * +* * +* Description : Traduit en chaîne de caractères une définition d'identité. * +* * +* Retour : Chaîne de caractères ou NULL. * +* * +* Remarques : - * +* * +******************************************************************************/ + +char *translate_x509_entries(const x509_entries *entries) +{ + char *result; /* Description à retourner */ + + result = NULL; + + if (entries->country != NULL) + { + result = stradd(result, "C="); + result = stradd(result, entries->country); + } + + if (entries->state != NULL) + { + if (result != NULL) result = stradd(result, "/"); + result = stradd(result, "ST="); + result = stradd(result, entries->state); + } + + if (entries->locality != NULL) + { + if (result != NULL) result = stradd(result, "/"); + result = stradd(result, "L="); + result = stradd(result, entries->locality); + } + + if (entries->organisation != NULL) + { + if (result != NULL) result = stradd(result, "/"); + result = stradd(result, "O="); + result = stradd(result, entries->organisation); + } + + if (entries->organisational_unit != NULL) + { + if (result != NULL) result = stradd(result, "/"); + result = stradd(result, "OU="); + result = stradd(result, entries->organisational_unit); + } + + if (entries->common_name != NULL) + { + if (result != NULL) result = stradd(result, "/"); + result = stradd(result, "CN="); + result = stradd(result, entries->common_name); + } + + return result; + +} + + +/****************************************************************************** +* * * Paramètres : entries = éléments d'identité à supprimer de la mémoire. * * * * Description : Libère la mémoire occupée par une définition d'identité. * @@ -255,6 +323,7 @@ static RSA *generate_rsa_key(unsigned int bits, unsigned long e) bool build_keys_and_ca(const char *dir, const char *label, unsigned long valid, const x509_entries *entries) { + bool result; /* Bilan à retourner */ RSA *rsa; /* Clef RSA pour le certificat */ EVP_PKEY *pk; /* Enveloppe pour clef publique*/ int ret; /* Bilan d'un appel */ @@ -263,20 +332,30 @@ bool build_keys_and_ca(const char *dir, const char *label, unsigned long valid, char *filename; /* Chemin d'accès à un fichier */ FILE *stream; /* Flux ouvert en écriture */ + result = false; + rsa = generate_rsa_key(4096, 17); if (rsa == NULL) goto rsa_failed; pk = EVP_PKEY_new(); - if (pk == NULL) goto pk_failed; + if (pk == NULL) + { + RSA_free(rsa); + goto pk_failed; + } ret = EVP_PKEY_assign_RSA(pk, rsa); - if (ret != 1) goto asign_failed; + if (ret != 1) + { + RSA_free(rsa); + goto asign_failed; + } x509 = X509_new(); if (x509 == NULL) goto x509_failed; ret = X509_set_pubkey(x509, pk); - if (ret != 1) goto ca_failed; + if (ret != 1) goto ca_asign_failed; ret = X509_set_version(x509, 2); if (ret != 1) goto ca_failed; @@ -344,7 +423,11 @@ bool build_keys_and_ca(const char *dir, const char *label, unsigned long valid, asprintf(&filename, "%s%c%s-key.pem", dir, G_DIR_SEPARATOR, label); stream = fopen(filename, "wb"); - if (stream == NULL) goto ca_failed; + if (stream == NULL) + { + free(filename); + goto ca_failed; + } ret = PEM_write_PrivateKey(stream, pk, NULL, NULL, 0, NULL, NULL); @@ -361,7 +444,11 @@ bool build_keys_and_ca(const char *dir, const char *label, unsigned long valid, asprintf(&filename, "%s%c%s-cert.pem", dir, G_DIR_SEPARATOR, label); stream = fopen(filename, "wb"); - if (stream == NULL) goto ca_failed; + if (stream == NULL) + { + free(filename); + goto ca_failed; + } ret = PEM_write_X509(stream, x509); @@ -375,34 +462,24 @@ bool build_keys_and_ca(const char *dir, const char *label, unsigned long valid, if (ret != 1) goto ca_failed; - /* Libérations finales */ - - X509_free(x509); - EVP_PKEY_free(pk); + result = true; - return true; + /* Libérations finales */ ca_failed: + ca_asign_failed: X509_free(x509); x509_failed: - - EVP_PKEY_free(pk); - - return false; - asign_failed: EVP_PKEY_free(pk); pk_failed: - - RSA_free(rsa); - rsa_failed: - return false; + return result; } @@ -458,6 +535,7 @@ static bool add_extension_to_req(STACK_OF(X509_EXTENSION) *sk, int nid, /*const bool build_keys_and_request(const char *dir, const char *label, const x509_entries *entries) { + bool result; /* Bilan à retourner */ RSA *rsa; /* Clef RSA pour le certificat */ EVP_PKEY *pk; /* Enveloppe pour clef publique*/ int ret; /* Bilan d'un appel */ @@ -467,20 +545,30 @@ bool build_keys_and_request(const char *dir, const char *label, const x509_entri char *filename; /* Chemin d'accès à un fichier */ FILE *stream; /* Flux ouvert en écriture */ + result = false; + rsa = generate_rsa_key(2048, 17); if (rsa == NULL) goto rsa_failed; pk = EVP_PKEY_new(); - if (pk == NULL) goto pk_failed; + if (pk == NULL) + { + RSA_free(rsa); + goto pk_failed; + } ret = EVP_PKEY_assign_RSA(pk, rsa); - if (ret != 1) goto asign_failed; + if (ret != 1) + { + RSA_free(rsa); + goto asign_failed; + } x509 = X509_REQ_new(); if (x509 == NULL) goto x509_failed; ret = X509_REQ_set_pubkey(x509, pk); - if (ret != 1) goto asign_failed; + if (ret != 1) goto req_asign_failed; /* Etablissement d'une identité */ @@ -523,8 +611,6 @@ bool build_keys_and_request(const char *dir, const char *label, const x509_entri ret = X509_REQ_add_extensions(x509, exts); if (ret != 1) goto exts_failed; - sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free); - /* Signature */ ret = X509_REQ_sign(x509, pk, EVP_sha256()); @@ -538,7 +624,11 @@ bool build_keys_and_request(const char *dir, const char *label, const x509_entri asprintf(&filename, "%s%c%s-key.pem", dir, G_DIR_SEPARATOR, label); stream = fopen(filename, "wb"); - if (stream == NULL) goto req_failed_2; + if (stream == NULL) + { + free(filename); + goto req_failed_2; + } ret = PEM_write_PrivateKey(stream, pk, NULL, NULL, 0, NULL, NULL); @@ -555,7 +645,11 @@ bool build_keys_and_request(const char *dir, const char *label, const x509_entri asprintf(&filename, "%s%c%s-csr.pem", dir, G_DIR_SEPARATOR, label); stream = fopen(filename, "wb"); - if (stream == NULL) goto req_failed_2; + if (stream == NULL) + { + free(filename); + goto req_failed_2; + } ret = PEM_write_X509_REQ(stream, x509); @@ -569,35 +663,87 @@ bool build_keys_and_request(const char *dir, const char *label, const x509_entri if (ret != 1) goto req_failed_2; - return true; + result = true; - req_failed_2: + /* Libérations finales */ + req_failed_2: exts_failed: - sk_X509_EXTENSION_free(exts); + sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free); req_failed: + req_asign_failed: X509_REQ_free(x509); x509_failed: + asign_failed: EVP_PKEY_free(pk); - return false; + pk_failed: + rsa_failed: - asign_failed: + return result; - EVP_PKEY_free(pk); +} - pk_failed: - RSA_free(rsa); +/****************************************************************************** +* * +* Paramètres : subject = sujet d'un élément X509. * +* entries = éléments de l'identité constituée. [OUT] * +* * +* Description : Recharge l'identité inscrite dans un élément X509. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool load_identity_from_x509(/*const */X509_NAME *subject, x509_entries *entries) +{ + bool result; /* Bilan à retourner */ + int length; /* Taille du champ visé */ - rsa_failed: + result = false; - return false; +#define GET_NAME_ENTRY(key, value) \ + do \ + { \ + length = X509_NAME_get_text_by_NID(subject, key, NULL, -1); \ + if (length != -1) \ + { \ + entries->value = malloc((length + 1) * sizeof(char)); \ + length = X509_NAME_get_text_by_NID(subject, key, entries->value, length + 1); \ + assert(length != -1); \ + if (length == -1) \ + goto copy_failed; \ + } \ + } \ + while (0) + + GET_NAME_ENTRY(NID_countryName, country); + + GET_NAME_ENTRY(NID_stateOrProvinceName, state); + + GET_NAME_ENTRY(NID_localityName, locality); + + GET_NAME_ENTRY(NID_organizationName, organisation); + + GET_NAME_ENTRY(NID_organizationalUnitName, organisational_unit); + + GET_NAME_ENTRY(NID_commonName, common_name); + +#undef GET_NAME_ENTRY + + result = true; + + copy_failed: + + return result; } @@ -621,7 +767,6 @@ bool load_identity_from_request(const char *csr, x509_entries *entries) FILE *stream; /* Flux ouvert en lecture */ X509_REQ *req; /* Certificat X509 à signer */ X509_NAME *subject; /* Sujet du certificat */ - int length; /* Taille du champ visé */ result = false; @@ -646,38 +791,65 @@ bool load_identity_from_request(const char *csr, x509_entries *entries) subject = X509_REQ_get_subject_name(req); -#define GET_NAME_ENTRY(key, value) \ - do \ - { \ - length = X509_NAME_get_text_by_NID(subject, key, NULL, -1); \ - if (length != -1) \ - { \ - entries->value = malloc((length + 1) * sizeof(char)); \ - length = X509_NAME_get_text_by_NID(subject, key, entries->value, length + 1); \ - assert(length != -1); \ - } \ - } \ - while (0) + result = load_identity_from_x509(subject, entries); - GET_NAME_ENTRY(NID_countryName, country); + X509_REQ_free(req); - GET_NAME_ENTRY(NID_stateOrProvinceName, state); + csr_read_failed: - GET_NAME_ENTRY(NID_localityName, locality); + return result; - GET_NAME_ENTRY(NID_organizationName, organisation); +} - GET_NAME_ENTRY(NID_organizationalUnitName, organisational_unit); - GET_NAME_ENTRY(NID_commonName, common_name); +/****************************************************************************** +* * +* Paramètres : crt = fichier contenant un certificat signé. * +* entries = éléments de l'identité constituée. [OUT] * +* * +* Description : Recharge l'identité inscrite dans un certificat signé. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ -#undef GET_NAME_ENTRY +bool load_identity_from_cert(const char *crt, x509_entries *entries) +{ + bool result; /* Bilan à retourner */ + FILE *stream; /* Flux ouvert en lecture */ + X509 *x; /* Certificat X509 signé */ + X509_NAME *subject; /* Sujet du certificat */ - X509_REQ_free(req); + result = false; - result = true; + memset(entries, 0, sizeof(*entries)); - csr_read_failed: + /* Chargement de la requête */ + + stream = fopen(crt, "rb"); + if (stream == NULL) goto crt_read_failed; + + x = PEM_read_X509(stream, NULL, NULL, NULL); + + fclose(stream); + + if (x == NULL) + { + log_variadic_message(LMT_ERROR, _("Unable to read the signed certificate from '%s'"), crt); + goto crt_read_failed; + } + + /* Recherche des éléments */ + + subject = X509_get_subject_name(x); + + result = load_identity_from_x509(subject, entries); + + X509_free(x); + + crt_read_failed: return result; @@ -714,7 +886,12 @@ bool sign_cert(const char *csr, const char *cacert, const char *cakey, const cha /* Chargement de la requête */ stream = fopen(csr, "rb"); - if (stream == NULL) goto csr_read_failed; + + if (stream == NULL) + { + log_variadic_message(LMT_ERROR, _("Unable to open the certificate signing request file '%s'"), csr); + goto csr_read_failed; + } req = PEM_read_X509_REQ(stream, NULL, NULL, NULL); @@ -722,7 +899,7 @@ bool sign_cert(const char *csr, const char *cacert, const char *cakey, const cha if (req == NULL) { - log_variadic_message(LMT_ERROR, _("Unable to read the certificate signing request from '%s'"), cert); + log_variadic_message(LMT_ERROR, _("Unable to read the certificate signing request from '%s'"), csr); goto csr_read_failed; } @@ -735,7 +912,12 @@ bool sign_cert(const char *csr, const char *cacert, const char *cakey, const cha /* Chargement des éléments de l'autorité */ stream = fopen(cacert, "rb"); - if (stream == NULL) goto cacert_read_failed; + + if (stream == NULL) + { + log_variadic_message(LMT_ERROR, _("Unable to open the CA certificate file '%s'"), cacert); + goto cacert_read_failed; + } ca_cert = PEM_read_X509(stream, NULL, NULL, NULL); @@ -743,12 +925,17 @@ bool sign_cert(const char *csr, const char *cacert, const char *cakey, const cha if (ca_cert == NULL) { - log_variadic_message(LMT_ERROR, _("Unable to read the certificate from '%s'"), cert); + log_variadic_message(LMT_ERROR, _("Unable to read the CA certificate from '%s'"), cacert); goto cacert_read_failed; } stream = fopen(cakey, "rb"); - if (stream == NULL) goto cakey_read_failed; + + if (stream == NULL) + { + log_variadic_message(LMT_ERROR, _("Unable to open the CA private key file '%s'"), cakey); + goto cakey_read_failed; + } ca_pk = PEM_read_PrivateKey(stream, NULL, NULL, NULL); @@ -756,7 +943,7 @@ bool sign_cert(const char *csr, const char *cacert, const char *cakey, const cha if (ca_pk == NULL) { - log_variadic_message(LMT_ERROR, _("Unable to read the CA private key from %s"), cakey); + log_variadic_message(LMT_ERROR, _("Unable to read the CA private key from '%s'"), cakey); goto cakey_read_failed; } diff --git a/src/analysis/db/certs.h b/src/analysis/db/certs.h index 51a2e34..359b3d3 100644 --- a/src/analysis/db/certs.h +++ b/src/analysis/db/certs.h @@ -45,6 +45,9 @@ typedef struct _x509_entries /* Indique si une définition existe dans l'identité. */ bool are_x509_entries_empty(const x509_entries *); +/* Traduit en chaîne de caractères une définition d'identité. */ +char *translate_x509_entries(const x509_entries *); + /* Libère la mémoire occupée par une définition d'identité. */ void free_x509_entries(x509_entries *); @@ -57,6 +60,9 @@ bool build_keys_and_request(const char *, const char *, const x509_entries *); /* Recharge l'identité inscrite dans une requête de signature. */ bool load_identity_from_request(const char *, x509_entries *); +/* Recharge l'identité inscrite dans un certificat signé. */ +bool load_identity_from_cert(const char *, x509_entries *); + /* Signe un certificat pour application. */ bool sign_cert(const char *, const char *, const char *, const char *, unsigned long); diff --git a/src/analysis/db/client-int.h b/src/analysis/db/client-int.h new file mode 100644 index 0000000..3c140e9 --- /dev/null +++ b/src/analysis/db/client-int.h @@ -0,0 +1,79 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * client.h - prototypes pour la connexion à un serveur Chrysalide + * + * Copyright (C) 2014-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/>. + */ + + +#ifndef _ANALYSIS_DB_CLIENT_INT_H +#define _ANALYSIS_DB_CLIENT_INT_H + + +#include "client.h" + + +#include "../../common/packed.h" + + + +/* Termine la constitution des données initiales à présenter. */ +typedef bool (* complete_client_hello_fc) (GHubClient *, packed_buffer_t *); + + +/* Description de client à l'écoute (instance) */ +struct _GHubClient +{ + GObject parent; /* A laisser en premier */ + + char *working; /* Répertoire de travail */ + + SSL_CTX *tls_ctx; /* Contexte du chiffrement */ + + int fd; /* Canal de communication */ + SSL *tls_fd; /* Même canal, mais sécurisé */ + char *desc; /* Description du lien */ + + GMutex sending_lock; /* Concurrence des envois */ + int stop_ctrl[2]; /* Commande d'arrêt */ + GThread *update; /* Procédure de traitement */ + +}; + +/* Description de client à l'écoute (classe) */ +struct _GHubClientClass +{ + GObjectClass parent; /* A laisser en premier */ + + uint32_t role; /* Rôle associé aux clients */ + complete_client_hello_fc complete_hello;/* Finalisation de l'intro */ + GThreadFunc recv_func; /* Réception de données */ + +}; + + + +/* Identifie le canal de communication pour envois au serveur. */ +SSL *g_hub_client_get_ssl_fd(GHubClient *); + +/* Marque le canal de communication comme disponible. */ +void g_hub_client_put_ssl_fd(GHubClient *, SSL *); + + + +#endif /* _ANALYSIS_DB_CLIENT_INT_H */ diff --git a/src/analysis/db/client.c b/src/analysis/db/client.c index ba0fec8..6ce33b0 100644 --- a/src/analysis/db/client.c +++ b/src/analysis/db/client.c @@ -37,6 +37,7 @@ #include <i18n.h> +#include "client-int.h" #include "auth.h" #include "protocol.h" #include "misc/rlestr.h" @@ -47,59 +48,6 @@ -/* Format générique des adresses de connexion */ -typedef union _gen_sockaddr_t -{ - struct sockaddr_in inet4_addr; /* Adresse d'écoute IPv4 */ - struct sockaddr_in6 inet6_addr; /* Adresse d'écoute IPv6 */ - struct sockaddr inet_4_6_addr; /* Adresse d'écoute IPv4/6 */ - -} gen_sockaddr_t; - - -/* Description de client à l'écoute (instance) */ -struct _GHubClient -{ - GObject parent; /* A laisser en premier */ - - rle_string hash; /* Empreinte du binaire lié */ - GList *collections; /* Collections d'un binaire */ - - char *working; /* Répertoire de travail */ - - SSL_CTX *tls_ctx; /* Contexte du chiffrement */ - - int fd; /* Canal de communication */ - SSL *tls_fd; /* Même canal, mais sécurisé */ - char *desc; /* Description du lien */ - - GMutex sending_lock; /* Concurrence des envois */ - bool can_get_updates; /* Réception de maj possibles ?*/ - GThread *update; /* Procédure de traitement */ - - snapshot_info_t *snapshots; /* Liste des instantanés */ - size_t snap_count; /* Taille de cette liste */ - GMutex snap_lock; /* Concurrence des accès */ - - snapshot_id_t current; /* Instantané courant */ - bool has_current; /* Validité de l'identifiant */ - GMutex cur_lock; /* Concurrence des accès */ - -}; - -/* Description de client à l'écoute (classe) */ -struct _GHubClientClass -{ - GObjectClass parent; /* A laisser en premier */ - - /* Signaux */ - - void (* snapshots_updated) (GHubClient *); - void (* snapshot_changed) (GHubClient *); - -}; - - /* Initialise la classe des descriptions de fichier binaire. */ static void g_hub_client_class_init(GHubClientClass *); @@ -112,23 +60,17 @@ static void g_hub_client_dispose(GHubClient *); /* Procède à la libération totale de la mémoire. */ static void g_hub_client_finalize(GHubClient *); -/* Démarre réellement la connexion à la base de données. */ -static bool g_hub_client_start_common(GHubClient *, char *); - -/* Assure l'accueil des nouvelles mises à jour. */ -static void *g_hub_client_update(GHubClient *); - -/* Met à jour la liste des instantanés courants. */ -static bool g_hub_client_update_snapshots(GHubClient *, packed_buffer *); - -/* Met à jour l'identifiant de l'instantané courant. */ -static bool g_hub_client_update_current_snapshot(GHubClient *, packed_buffer *); +/* Format générique des adresses de connexion */ +typedef union _gen_sockaddr_t +{ + struct sockaddr_in inet4_addr; /* Adresse d'écoute IPv4 */ + struct sockaddr_in6 inet6_addr; /* Adresse d'écoute IPv6 */ + struct sockaddr inet_4_6_addr; /* Adresse d'écoute IPv4/6 */ -/* Identifie le canal de communication pour envois au serveur. */ -static SSL *g_hub_client_get_ssl_fd(GHubClient *); +} gen_sockaddr_t; -/* Marque le canal de communication comme disponible. */ -static void g_hub_client_put_ssl_fd(GHubClient *, SSL *); +/* Démarre réellement la connexion à la base de données. */ +static bool g_hub_client_start_common(GHubClient *, char *); @@ -157,21 +99,9 @@ static void g_hub_client_class_init(GHubClientClass *klass) object->dispose = (GObjectFinalizeFunc/* ! */)g_hub_client_dispose; object->finalize = (GObjectFinalizeFunc)g_hub_client_finalize; - g_signal_new("snapshots-updated", - G_TYPE_HUB_CLIENT, - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET(GHubClientClass, snapshots_updated), - NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, 0); - - g_signal_new("snapshot-changed", - G_TYPE_HUB_CLIENT, - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET(GHubClientClass, snapshot_changed), - NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, 0); + klass->role = CRL_UNDEFINED; + klass->complete_hello = NULL; + klass->recv_func = NULL; } @@ -190,9 +120,6 @@ static void g_hub_client_class_init(GHubClientClass *klass) static void g_hub_client_init(GHubClient *client) { - setup_empty_rle_string(&client->hash); - client->collections = NULL; - client->working = NULL; client->tls_ctx = NULL; @@ -202,17 +129,10 @@ static void g_hub_client_init(GHubClient *client) client->desc = NULL; g_mutex_init(&client->sending_lock); - client->can_get_updates = false; + client->stop_ctrl[0] = -1; + client->stop_ctrl[1] = -1; client->update = NULL; - client->snapshots = NULL; - client->snap_count = 0; - g_mutex_init(&client->snap_lock); - - setup_empty_snapshot_id(&client->current); - client->has_current = false; - g_mutex_init(&client->cur_lock); - } @@ -230,12 +150,6 @@ static void g_hub_client_init(GHubClient *client) static void g_hub_client_dispose(GHubClient *client) { - g_hub_client_stop(client); - - g_mutex_clear(&client->cur_lock); - - g_mutex_clear(&client->snap_lock); - g_mutex_clear(&client->sending_lock); G_OBJECT_CLASS(g_hub_client_parent_class)->dispose(G_OBJECT(client)); @@ -257,10 +171,6 @@ static void g_hub_client_dispose(GHubClient *client) static void g_hub_client_finalize(GHubClient *client) { - size_t i; /* Boucle de parcours */ - - unset_rle_string(&client->hash); - if (client->working != NULL) free(client->working); @@ -270,15 +180,6 @@ static void g_hub_client_finalize(GHubClient *client) if (client->desc != NULL) free(client->desc); - if (client->snapshots != NULL) - { - for (i = 0; i < client->snap_count; i++) - exit_snapshot_info(&client->snapshots[i]); - - free(client->snapshots); - - } - G_OBJECT_CLASS(g_hub_client_parent_class)->finalize(G_OBJECT(client)); } @@ -286,33 +187,6 @@ static void g_hub_client_finalize(GHubClient *client) /****************************************************************************** * * -* Paramètres : hash = empreinte d'un binaire en cours d'analyse. * -* collections = ensemble de collections existantes. * -* * -* Description : Prépare un client pour une connexion à une BD. * -* * -* Retour : Structure mise en place ou NULL en cas d'échec. * -* * -* Remarques : - * -* * -******************************************************************************/ - -GHubClient *g_hub_client_new(const char *hash, GList *collections) -{ - GHubClient *result; /* Adresse à retourner */ - - result = g_object_new(G_TYPE_HUB_CLIENT, NULL); - - init_static_rle_string(&result->hash, hash); - result->collections = collections; - - return result; - -} - - -/****************************************************************************** -* * * Paramètres : client = client pour les accès distants à manipuler. * * * * Description : Démarre la connexion à la base de données interne. * @@ -522,9 +396,10 @@ static bool g_hub_client_start_common(GHubClient *client, char *desc) char *filename; /* Fichier PEM à manipuler */ int ret; /* Bilan d'un appel */ char *rootdir; /* Racine pour le client */ - packed_buffer out_pbuf; /* Tampon d'émission */ + GHubClientClass *class; /* Classe du client connecté */ + packed_buffer_t out_pbuf; /* Tampon d'émission */ bool status; /* Bilan d'une opération */ - packed_buffer in_pbuf; /* Tampon de réception */ + packed_buffer_t in_pbuf; /* Tampon de réception */ uint32_t data; /* Mot de données lues */ DBError error; /* Validation de la connexion */ @@ -608,11 +483,14 @@ static bool g_hub_client_start_common(GHubClient *client, char *desc) goto ssl_error; } + class = G_HUB_CLIENT_GET_CLASS(client); + /** * On réalise l'envoi initial ; le premier paquet doit contenir : - * - la commande 'DBC_HELO'. - * - le numéro de version du client. - * - l'empreinte du binaire analysé. + * - la commande 'DBC_HELO' ; + * - le numéro de version du client ; + * - le rôle attendu de la connexion ; + * - des données complémentaires éventuelles. * * Tout ceci est à synchroniser avec la fonction g_db_server_listener(). */ @@ -625,15 +503,21 @@ static bool g_hub_client_start_common(GHubClient *client, char *desc) status = extend_packed_buffer(&out_pbuf, (uint32_t []) { CDB_PROTOCOL_VERSION }, sizeof(uint32_t), true); if (!status) goto setup_error; - status = pack_rle_string(&client->hash, &out_pbuf); + status = extend_packed_buffer(&out_pbuf, &class->role, sizeof(uint32_t), true); if (!status) goto setup_error; + if (class->complete_hello != NULL) + { + status = class->complete_hello(client, &out_pbuf); + if (!status) goto setup_error; + } + status = ssl_send_packed_buffer(&out_pbuf, client->tls_fd); if (!status) goto setup_error; /** * Le serveur doit répondre pour un message type : - * - la commande 'DBC_WELCOME'. + * - la commande 'DBC_WELCOME' ; * - un identifiant d'erreur ('DBE_NONE', 'DBE_BAD_EXCHANGE' * ou 'DBE_WRONG_VERSION' ... 'DBE_LOADING_ERROR'). */ @@ -689,14 +573,19 @@ static bool g_hub_client_start_common(GHubClient *client, char *desc) } - client->can_get_updates = false; + ret = pipe(client->stop_ctrl); + if (ret != 0) + { + LOG_ERROR_N("pipe"); + goto sys_error; + } - client->update = g_thread_try_new("cdb_client", (GThreadFunc)g_hub_client_update, client, NULL); + client->update = g_thread_try_new("cdb_client", class->recv_func, client, NULL); if (client->update == NULL) { log_variadic_message(LMT_ERROR, _("Failed to start a listening thread for the server '%s'!"), desc); - goto comm_error; + goto sys_error; } exit_packed_buffer(&out_pbuf); @@ -704,6 +593,7 @@ static bool g_hub_client_start_common(GHubClient *client, char *desc) return true; + sys_error: comm_error: exit_packed_buffer(&in_pbuf); @@ -733,344 +623,6 @@ static bool g_hub_client_start_common(GHubClient *client, char *desc) * * * Paramètres : client = client pour les accès distants à manipuler. * * * -* Description : Assure l'accueil des nouvelles mises à jour. * -* * -* Retour : NULL. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void *g_hub_client_update(GHubClient *client) -{ - packed_buffer out_pbuf; /* Tampon d'émission */ - bool status; /* Bilan d'une opération */ - struct pollfd fds; /* Surveillance des flux */ - packed_buffer in_pbuf; /* Tampon de réception */ - int ret; /* Bilan d'un appel */ - uint32_t tmp32; /* Valeur sur 32 bits */ - uint32_t command; /* Commande de la requête */ - DBError error; /* Bilan d'une commande passée */ - GDbCollection *collec; /* Collection visée au final */ - uint8_t tmp8; /* Valeur sur 8 bits */ - char *msg; /* Message d'erreur à imprimer */ - - /** - * Avant toute chose, on demande un stage d'actualisation ! - */ - - init_packed_buffer(&out_pbuf); - - status = extend_packed_buffer(&out_pbuf, (uint32_t []) { DBC_GET_SNAPSHOTS }, sizeof(uint32_t), true); - if (!status) - { - exit_packed_buffer(&out_pbuf); - goto exit; - } - - status = extend_packed_buffer(&out_pbuf, (uint32_t []) { DBC_GET_CUR_SNAPSHOT }, sizeof(uint32_t), true); - if (!status) - { - exit_packed_buffer(&out_pbuf); - goto exit; - } - - status = extend_packed_buffer(&out_pbuf, (uint32_t []) { DBC_GET_ALL_ITEMS }, sizeof(uint32_t), true); - if (!status) - { - exit_packed_buffer(&out_pbuf); - goto exit; - } - - status = ssl_send_packed_buffer(&out_pbuf, client->tls_fd); - if (!status) - { - log_simple_message(LMT_INFO, _("Failed to get all updates")); - exit_packed_buffer(&out_pbuf); - goto exit; - } - - exit_packed_buffer(&out_pbuf); - - /** - * Phase d'écoute continue... - */ - - fds.fd = client->fd; - fds.events = POLLIN | POLLPRI; - - init_packed_buffer(&in_pbuf); - - while (client->fd != -1) - { - ret = poll(&fds, 1, -1); - if (ret != 1) continue; - - /* Le canal est fermé, une sortie doit être demandée... */ - if (fds.revents & POLLNVAL) - break; - - /** - * Même chose, cf. "TCP: When is EPOLLHUP generated?" - * https://stackoverflow.com/questions/52976152/tcp-when-is-epollhup-generated/52976327#52976327 - */ - - if (fds.revents & (POLLHUP | POLLRDHUP)) - break; - - if (fds.revents & (POLLIN | POLLPRI)) - { - reset_packed_buffer(&in_pbuf); - - status = ssl_recv_packed_buffer(&in_pbuf, client->tls_fd); - if (!status) goto gdcu_bad_exchange; - - next_command: - - status = extract_packed_buffer(&in_pbuf, &command, sizeof(uint32_t), true); - if (!status) goto gdcu_bad_exchange; - - switch (command) - { - case DBC_SAVE: - - status = extract_packed_buffer(&in_pbuf, &tmp32, sizeof(uint32_t), true); - if (!status) goto gdcu_bad_exchange; - - error = tmp32; - - if (error == DBE_NONE) - log_variadic_message(LMT_INFO, _("Archive saved for binary '%s'"), - get_rle_string(&client->hash)); - else - log_variadic_message(LMT_ERROR, _("Failed to save the archive for binary '%s'"), - get_rle_string(&client->hash)); - - break; - - case DBC_COLLECTION: - - status = extract_packed_buffer(&in_pbuf, &tmp32, sizeof(uint32_t), true); - if (!status) goto gdcu_bad_exchange; - - collec = find_collection_in_list(client->collections, tmp32); - if (collec == NULL) goto gdcu_bad_exchange; - - if (client->can_get_updates) - status = g_db_collection_unpack(collec, &in_pbuf, NULL); - else - status = _g_db_collection_unpack(collec, &in_pbuf, (DBAction []) { 0 }, NULL); - - if (!status) goto gdcu_bad_exchange; - - break; - - case DBC_GET_ALL_ITEMS: - log_variadic_message(LMT_INFO, - _("This command is not available on this side: 0x%08x"), command); - goto gdcu_bad_exchange; - break; - - case DBC_SET_ALL_ITEMS: - - status = extract_packed_buffer(&in_pbuf, &tmp8, sizeof(uint8_t), true); - if (!status) goto gdcu_bad_exchange; - - client->can_get_updates = (tmp8 == 0x1); - break; - - case DBC_GET_SNAPSHOTS: - log_variadic_message(LMT_INFO, - _("This command is not available on this side: 0x%08x"), command); - goto gdcu_bad_exchange; - break; - - case DBC_SNAPSHOTS_UPDATED: - - status = g_hub_client_update_snapshots(client, &in_pbuf); - if (!status) goto gdcu_bad_exchange; - - break; - - case DBC_GET_CUR_SNAPSHOT: - log_variadic_message(LMT_INFO, - _("This command is not available on this side: 0x%08x"), command); - goto gdcu_bad_exchange; - break; - - case DBC_CUR_SNAPSHOT_UPDATED: - - status = g_hub_client_update_current_snapshot(client, &in_pbuf); - if (!status) goto gdcu_bad_exchange; - - break; - - case DBC_SET_CUR_SNAPSHOT: - case DBC_SET_SNAPSHOT_NAME: - case DBC_SET_SNAPSHOT_DESC: - case DBC_CREATE_SNAPSHOT: - case DBC_REMOVE_SNAPSHOT: - log_variadic_message(LMT_INFO, - _("This command is not available on this side: 0x%08x"), command); - goto gdcu_bad_exchange; - break; - - } - - if (has_more_data_in_packed_buffer(&in_pbuf)) - goto next_command; - - client->can_get_updates = true; - continue; - - gdcu_bad_exchange: - - asprintf(&msg, _("Bad reception from %s"), client->desc); - - LOG_ERROR(LMT_ERROR, msg); - - free(msg); - - break; - - } - - } - - exit: - - g_hub_client_stop(client); - - exit_packed_buffer(&in_pbuf); - - return NULL; - -} - - -/****************************************************************************** -* * -* Paramètres : client = client pour les accès distants à manipuler. * -* pbuf = données présentes à traiter. * -* * -* Description : Met à jour la liste des instantanés courants. * -* * -* Retour : true si l'opération s'est déroulée sans encombre, ou false. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static bool g_hub_client_update_snapshots(GHubClient *client, packed_buffer *pbuf) -{ - bool result; /* Validité à retourner */ - size_t i; /* Boucle de parcours */ - char id[SNAP_ID_HEX_SZ]; /* Caractères hexadécimaux */ - snapshot_info_t info; /* Description d'instantané */ - snapshot_info_t *dest; /* Destination de description */ - - result = true; - - g_mutex_lock(&client->snap_lock); - - if (client->snapshots != NULL) - { - for (i = 0; i < client->snap_count; i++) - exit_snapshot_info(&client->snapshots[i]); - - free(client->snapshots); - - client->snapshots = NULL; - client->snap_count = 0; - - } - - do - { - result = peek_packed_buffer(pbuf, id, SNAP_ID_HEX_SZ, false); - if (!result) break; - - if (strncmp(id, SNAPSHOT_END_MARK, SNAP_ID_HEX_SZ) == 0) - { - advance_packed_buffer(pbuf, SNAP_ID_HEX_SZ); - break; - } - - else - { - setup_empty_snapshot_info(&info); - - result = unpack_snapshot_info(&info, pbuf); - if (!result) break; - - client->snapshots = realloc(client->snapshots, ++client->snap_count * sizeof(snapshot_info_t)); - - dest = &client->snapshots[client->snap_count - 1]; - - setup_empty_snapshot_info(dest); - copy_snapshot_info(dest, &info); - - exit_snapshot_info(&info); - - } - - } - while (true); - - g_mutex_unlock(&client->snap_lock); - - if (result) - g_signal_emit_by_name(client, "snapshots-updated"); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : client = client pour les accès distants à manipuler. * -* pbuf = données présentes à traiter. * -* * -* Description : Met à jour l'identifiant de l'instantané courant. * -* * -* Retour : true si l'opération s'est déroulée sans encombre, ou false. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static bool g_hub_client_update_current_snapshot(GHubClient *client, packed_buffer *pbuf) -{ - bool result; /* Validité à retourner */ - snapshot_id_t id; /* Identifiant d'instantané */ - - setup_empty_snapshot_id(&id); - - result = unpack_snapshot_id(&id, pbuf); - - if (result) - { - g_mutex_lock(&client->cur_lock); - - copy_snapshot_id(&client->current, &id); - client->has_current = true; - - g_mutex_unlock(&client->cur_lock); - - g_signal_emit_by_name(client, "snapshot-changed"); - - } - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : client = client pour les accès distants à manipuler. * -* * * Description : Arrête la connexion à la base de données. * * * * Retour : - * @@ -1083,8 +635,9 @@ void g_hub_client_stop(GHubClient *client) { int fd; /* Canal à clôturer */ int ret; /* Bilan d'un appel */ + ssize_t sent; /* Quantité de données émises */ - /* Canal de communication */ + /* Gestion du double appel */ if (client->fd == -1) { @@ -1103,13 +656,18 @@ void g_hub_client_stop(GHubClient *client) client->fd = -1; - ret = close(fd); - if (ret == -1) LOG_ERROR_N("close"); + /* Ordre d'arrêt */ if (g_thread_self() != client->update) + { + sent = write(client->stop_ctrl[1], "\xf0", 1); + if (sent != 1) LOG_ERROR_N("write"); + g_thread_join(client->update); - /* Environnement TLS */ + } + + /* Fermeture des flux */ SSL_free(client->tls_fd); client->tls_fd = NULL; @@ -1117,6 +675,17 @@ void g_hub_client_stop(GHubClient *client) SSL_CTX_free(client->tls_ctx); client->tls_ctx = NULL; + ret = close(fd); + if (ret == -1) LOG_ERROR_N("close"); + + ret = close(client->stop_ctrl[0]); + if (ret == -1) LOG_ERROR_N("close"); + client->stop_ctrl[0] = -1; + + ret = close(client->stop_ctrl[1]); + if (ret == -1) LOG_ERROR_N("close"); + client->stop_ctrl[1] = -1; + } @@ -1132,7 +701,7 @@ void g_hub_client_stop(GHubClient *client) * * ******************************************************************************/ -static SSL *g_hub_client_get_ssl_fd(GHubClient *client) +SSL *g_hub_client_get_ssl_fd(GHubClient *client) { SSL *result; /* Canal à retourner */ #ifndef NDEBUG @@ -1174,541 +743,10 @@ static SSL *g_hub_client_get_ssl_fd(GHubClient *client) * * ******************************************************************************/ -static void g_hub_client_put_ssl_fd(GHubClient *client, SSL *tls_fd) +void g_hub_client_put_ssl_fd(GHubClient *client, SSL *tls_fd) { g_mutex_unlock(&client->sending_lock); SSL_free(tls_fd); } - - -/****************************************************************************** -* * -* Paramètres : client = client pour les accès distants à manipuler. * -* * -* Description : Effectue une demande de sauvegarde de l'état courant. * -* * -* Retour : true si la commande a bien été envoyée, false sinon. * -* * -* Remarques : - * -* * -******************************************************************************/ - -bool g_hub_client_save(GHubClient *client) -{ - bool result; /* Bilan partiel à remonter */ - packed_buffer out_pbuf; /* Tampon d'émission */ - SSL *tls_fd; /* Canal de communication SSL */ - - init_packed_buffer(&out_pbuf); - - tls_fd = g_hub_client_get_ssl_fd(client); - - if (tls_fd == NULL) - result = false; - - else - { - result = extend_packed_buffer(&out_pbuf, (uint32_t []) { DBC_SAVE }, sizeof(uint32_t), true); - - if (result) - result = ssl_send_packed_buffer(&out_pbuf, tls_fd); - - g_hub_client_put_ssl_fd(client, tls_fd); - - } - - exit_packed_buffer(&out_pbuf); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : client = client pour les accès distants à manipuler. * -* item = élémnent à pousser vers un serveur de collection. * -* * -* Description : Ajoute un élément à la collection d'un serveur. * -* * -* Retour : true si la commande a bien été envoyée, false sinon. * -* * -* Remarques : - * -* * -******************************************************************************/ - -bool g_hub_client_add_item(GHubClient *client, const GDbItem *item) -{ - bool result; /* Bilan partiel à remonter */ - packed_buffer out_pbuf; /* Tampon d'émission */ - SSL *tls_fd; /* Canal de communication SSL */ - DBFeatures feature; /* Domaine de fonctionnalité */ - GDbCollection *collec; /* Collection visée au final */ - - init_packed_buffer(&out_pbuf); - - tls_fd = g_hub_client_get_ssl_fd(client); - - if (tls_fd == NULL) - result = false; - - else - { - feature = g_db_item_get_feature(item); - - collec = find_collection_in_list(client->collections, feature); - if (collec == NULL) - { - result = false; - goto bad_item_feature; - } - - result = g_db_collection_pack(collec, &out_pbuf, DBA_ADD_ITEM, item); - - if (result) - result = ssl_send_packed_buffer(&out_pbuf, tls_fd); - - bad_item_feature: - - g_hub_client_put_ssl_fd(client, tls_fd); - - } - - exit_packed_buffer(&out_pbuf); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : client = client pour les accès distants à manipuler. * -* timestamp = date du dernier élément à garder comme actif. * -* * -* Description : Active les éléments en amont d'un horodatage donné. * -* * -* Retour : true si la commande a bien été envoyée, false sinon. * -* * -* Remarques : - * -* * -******************************************************************************/ - -bool g_hub_client_set_last_active(GHubClient *client, timestamp_t timestamp) -{ - bool result; /* Bilan partiel à remonter */ - packed_buffer out_pbuf; /* Tampon d'émission */ - SSL *tls_fd; /* Canal de communication SSL */ - - init_packed_buffer(&out_pbuf); - - tls_fd = g_hub_client_get_ssl_fd(client); - - if (tls_fd == NULL) - result = false; - - else - { - result = extend_packed_buffer(&out_pbuf, (uint32_t []) { DBC_SET_LAST_ACTIVE }, sizeof(uint32_t), true); - - if (result) - result = pack_timestamp(×tamp, &out_pbuf); - - if (result) - result = ssl_send_packed_buffer(&out_pbuf, tls_fd); - - g_hub_client_put_ssl_fd(client, tls_fd); - - } - - exit_packed_buffer(&out_pbuf); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : client = client pour les accès distants à manipuler. * -* info = description des instantanés présents. [OUT] * -* count = taille de la liste retournée. [OUT] * -* * -* Description : Fournit la liste des instantanés existants. * -* * -* Retour : true si la liste retournée est valide, false sinon. * -* * -* Remarques : - * -* * -******************************************************************************/ - -bool g_hub_client_get_snapshots(GHubClient *client, snapshot_info_t **info, size_t *count) -{ - bool result; /* Validité à retourner */ - size_t i; /* Boucle de parcours */ - snapshot_info_t *dest; /* Destination de description */ - - g_mutex_lock(&client->snap_lock); - - result = (client->snap_count > 0); - - if (result) - { - *info = malloc(client->snap_count * sizeof(snapshot_info_t)); - *count = client->snap_count; - - for (i = 0; i < client->snap_count; i++) - { - dest = &(*info)[i]; - - setup_empty_snapshot_info(dest); - copy_snapshot_info(dest, &client->snapshots[i]); - - } - - } - - g_mutex_unlock(&client->snap_lock); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : client = client pour les accès distants à manipuler. * -* id = identifiant d'instantané à renseigner. [OUT] * -* * -* Description : Fournit l'identifiant de l'instantané courant. * -* * -* Retour : true si l'identifiant retourné est valide, false sinon. * -* * -* Remarques : - * -* * -******************************************************************************/ - -bool g_hub_client_get_current_snapshot(GHubClient *client, snapshot_id_t *id) -{ - bool result; /* Validité à retourner */ - - g_mutex_lock(&client->cur_lock); - - result = client->has_current; - - if (result) - copy_snapshot_id(id, &client->current); - - g_mutex_unlock(&client->cur_lock); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : client = client pour les accès distants à manipuler. * -* id = identifiant d'instantané à activer. * -* * -* Description : Définit l'identifiant de l'instantané courant. * -* * -* Retour : true si la commande a bien été envoyée, false sinon. * -* * -* Remarques : - * -* * -******************************************************************************/ - -bool g_hub_client_set_current_snapshot(GHubClient *client, const snapshot_id_t *id) -{ - bool result; /* Bilan partiel à remonter */ - packed_buffer out_pbuf; /* Tampon d'émission */ - SSL *tls_fd; /* Canal de communication SSL */ - - init_packed_buffer(&out_pbuf); - - tls_fd = g_hub_client_get_ssl_fd(client); - - if (tls_fd == NULL) - result = false; - - else - { - result = extend_packed_buffer(&out_pbuf, (uint32_t []) { DBC_SET_CUR_SNAPSHOT }, sizeof(uint32_t), true); - - if (result) - result = pack_snapshot_id(id, &out_pbuf); - - if (result) - result = ssl_send_packed_buffer(&out_pbuf, tls_fd); - - g_hub_client_put_ssl_fd(client, tls_fd); - - } - - exit_packed_buffer(&out_pbuf); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : client = client pour les accès distants à manipuler. * -* id = identifiant d'instantané à traiter. * -* name = désignation humaine pour l'instantané. * -* * -* Description : Définit la désignation d'un instantané donné. * -* * -* Retour : true si la commande a bien été envoyée, false sinon. * -* * -* Remarques : - * -* * -******************************************************************************/ - -bool g_hub_client_set_snapshot_name(GHubClient *client, const snapshot_id_t *id, const char *name) -{ - bool result; /* Bilan partiel à remonter */ - packed_buffer out_pbuf; /* Tampon d'émission */ - SSL *tls_fd; /* Canal de communication SSL */ - rle_string string; /* Chaîne à transmettre */ - - init_packed_buffer(&out_pbuf); - - tls_fd = g_hub_client_get_ssl_fd(client); - - if (tls_fd == NULL) - result = false; - - else - { - result = extend_packed_buffer(&out_pbuf, (uint32_t []) { DBC_SET_SNAPSHOT_NAME }, sizeof(uint32_t), true); - - if (result) - result = pack_snapshot_id(id, &out_pbuf); - - if (result) - { - init_static_rle_string(&string, name); - - result = pack_rle_string(&string, &out_pbuf); - - exit_rle_string(&string); - - } - - if (result) - result = ssl_send_packed_buffer(&out_pbuf, tls_fd); - - g_hub_client_put_ssl_fd(client, tls_fd); - - } - - exit_packed_buffer(&out_pbuf); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : client = client pour les accès distants à manipuler. * -* id = identifiant d'instantané à traiter. * -* desc = description humaine pour l'instantané. * -* * -* Description : Définit la description d'un instantané donné. * -* * -* Retour : true si la commande a bien été envoyée, false sinon. * -* * -* Remarques : - * -* * -******************************************************************************/ - -bool g_hub_client_set_snapshot_desc(GHubClient *client, const snapshot_id_t *id, const char *desc) -{ - bool result; /* Bilan partiel à remonter */ - packed_buffer out_pbuf; /* Tampon d'émission */ - SSL *tls_fd; /* Canal de communication SSL */ - rle_string string; /* Chaîne à transmettre */ - - init_packed_buffer(&out_pbuf); - - tls_fd = g_hub_client_get_ssl_fd(client); - - if (tls_fd == NULL) - result = false; - - else - { - result = extend_packed_buffer(&out_pbuf, (uint32_t []) { DBC_SET_SNAPSHOT_DESC }, sizeof(uint32_t), true); - - if (result) - result = pack_snapshot_id(id, &out_pbuf); - - if (result) - { - init_static_rle_string(&string, desc); - - result = pack_rle_string(&string, &out_pbuf); - - exit_rle_string(&string); - - } - - if (result) - result = ssl_send_packed_buffer(&out_pbuf, tls_fd); - - g_hub_client_put_ssl_fd(client, tls_fd); - - } - - exit_packed_buffer(&out_pbuf); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : client = client pour les accès distants à manipuler. * -* id = identifiant d'instantané à traiter. * -* * -* Description : Restaure un ancien instantané. * -* * -* Retour : true si la commande a bien été envoyée, false sinon. * -* * -* Remarques : - * -* * -******************************************************************************/ - -bool g_hub_client_restore_snapshot(GHubClient *client, const snapshot_id_t *id) -{ - bool result; /* Bilan partiel à remonter */ - packed_buffer out_pbuf; /* Tampon d'émission */ - SSL *tls_fd; /* Canal de communication SSL */ - - init_packed_buffer(&out_pbuf); - - tls_fd = g_hub_client_get_ssl_fd(client); - - if (tls_fd == NULL) - result = false; - - else - { - result = extend_packed_buffer(&out_pbuf, (uint32_t []) { DBC_SET_CUR_SNAPSHOT }, sizeof(uint32_t), true); - - if (result) - result = pack_snapshot_id(id, &out_pbuf); - - if (result) - result = ssl_send_packed_buffer(&out_pbuf, tls_fd); - - g_hub_client_put_ssl_fd(client, tls_fd); - - } - - exit_packed_buffer(&out_pbuf); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : client = client pour les accès distants à manipuler. * -* * -* Description : Crée un nouvel instantané à partir d'un autre. * -* * -* Retour : true si la commande a bien été envoyée, false sinon. * -* * -* Remarques : - * -* * -******************************************************************************/ - -bool g_hub_client_create_snapshot(GHubClient *client) -{ - bool result; /* Bilan partiel à remonter */ - packed_buffer out_pbuf; /* Tampon d'émission */ - SSL *tls_fd; /* Canal de communication SSL */ - - init_packed_buffer(&out_pbuf); - - tls_fd = g_hub_client_get_ssl_fd(client); - - if (tls_fd == NULL) - result = false; - - else - { - result = extend_packed_buffer(&out_pbuf, (uint32_t []) { DBC_CREATE_SNAPSHOT }, sizeof(uint32_t), true); - - if (result) - result = ssl_send_packed_buffer(&out_pbuf, tls_fd); - - g_hub_client_put_ssl_fd(client, tls_fd); - - } - - exit_packed_buffer(&out_pbuf); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : client = client pour les accès distants à manipuler. * -* id = identifiant d'instantané à traiter. * -* rec = programme une suppression récursive. * -* * -* Description : Supprime un ancien instantané. * -* * -* Retour : true si la commande a bien été envoyée, false sinon. * -* * -* Remarques : - * -* * -******************************************************************************/ - -bool g_hub_client_remove_snapshot(GHubClient *client, const snapshot_id_t *id, bool rec) -{ - bool result; /* Bilan partiel à remonter */ - packed_buffer out_pbuf; /* Tampon d'émission */ - SSL *tls_fd; /* Canal de communication SSL */ - - init_packed_buffer(&out_pbuf); - - tls_fd = g_hub_client_get_ssl_fd(client); - - if (tls_fd == NULL) - result = false; - - else - { - result = extend_packed_buffer(&out_pbuf, (uint32_t []) { DBC_REMOVE_SNAPSHOT }, sizeof(uint32_t), true); - - if (result) - result = pack_snapshot_id(id, &out_pbuf); - - if (result) - result = extend_packed_buffer(&out_pbuf, (uint8_t []) { rec ? 0x1 : 0x0 }, sizeof(uint8_t), false); - - if (result) - result = ssl_send_packed_buffer(&out_pbuf, tls_fd); - - g_hub_client_put_ssl_fd(client, tls_fd); - - } - - exit_packed_buffer(&out_pbuf); - - return result; - -} diff --git a/src/analysis/db/client.h b/src/analysis/db/client.h index 9a95163..0ec40e6 100644 --- a/src/analysis/db/client.h +++ b/src/analysis/db/client.h @@ -30,9 +30,6 @@ #include <openssl/ssl.h> -#include "collection.h" -#include "misc/snapshot.h" - #define G_TYPE_HUB_CLIENT g_hub_client_get_type() @@ -53,9 +50,6 @@ typedef struct _GHubClientClass GHubClientClass; /* Indique le type défini pour une description de client à l'écoute. */ GType g_hub_client_get_type(void); -/* Prépare un client pour une connexion à une BD. */ -GHubClient *g_hub_client_new(const char *, GList *); - /* Démarre la connexion à la base de données interne. */ bool g_hub_client_start_internal(GHubClient *); @@ -65,39 +59,6 @@ bool g_hub_client_start_remote(GHubClient *, const char *, const char *, bool); /* Arrête la connexion à la base de données. */ void g_hub_client_stop(GHubClient *); -/* Effectue une demande de sauvegarde de l'état courant. */ -bool g_hub_client_save(GHubClient *); - -/* Ajoute un élément à la collection d'un serveur. */ -bool g_hub_client_add_item(GHubClient *, const GDbItem *); - -/* Active les éléments en amont d'un horodatage donné. */ -bool g_hub_client_set_last_active(GHubClient *, timestamp_t); - -/* Fournit la liste des instantanés existants. */ -bool g_hub_client_get_snapshots(GHubClient *, snapshot_info_t **, size_t *); - -/* Fournit l'identifiant de l'instantané courant. */ -bool g_hub_client_get_current_snapshot(GHubClient *, snapshot_id_t *); - -/* Définit l'identifiant de l'instantané courant. */ -bool g_hub_client_set_current_snapshot(GHubClient *, const snapshot_id_t *); - -/* Définit la désignation d'un instantané donné. */ -bool g_hub_client_set_snapshot_name(GHubClient *, const snapshot_id_t *, const char *); - -/* Définit la description d'un instantané donné. */ -bool g_hub_client_set_snapshot_desc(GHubClient *, const snapshot_id_t *, const char *); - -/* Restaure un ancien instantané. */ -bool g_hub_client_restore_snapshot(GHubClient *, const snapshot_id_t *); - -/* Crée un nouvel instantané à partir d'un autre. */ -bool g_hub_client_create_snapshot(GHubClient *); - -/* Supprime un ancien instantané. */ -bool g_hub_client_remove_snapshot(GHubClient *, const snapshot_id_t *, bool); - #endif /* _ANALYSIS_DB_CLIENT_H */ diff --git a/src/analysis/db/collection.c b/src/analysis/db/collection.c index dcab73e..52476dd 100644 --- a/src/analysis/db/collection.c +++ b/src/analysis/db/collection.c @@ -324,7 +324,7 @@ const char *g_db_collection_get_name(const GDbCollection *collec) * * ******************************************************************************/ -bool _g_db_collection_unpack(GDbCollection *collec, packed_buffer *pbuf, DBAction *action, GDbItem **dest) +bool _g_db_collection_unpack(GDbCollection *collec, packed_buffer_t *pbuf, DBAction *action, GDbItem **dest) { bool result; /* Bilan à faire remonter */ uint32_t tmp32; /* Valeur sur 32 bits */ @@ -376,7 +376,7 @@ bool _g_db_collection_unpack(GDbCollection *collec, packed_buffer *pbuf, DBActio * * ******************************************************************************/ -bool g_db_collection_unpack(GDbCollection *collec, packed_buffer *pbuf, sqlite3 *db) +bool g_db_collection_unpack(GDbCollection *collec, packed_buffer_t *pbuf, sqlite3 *db) { bool result; /* Bilan à faire remonter */ DBAction action; /* Commande de la requête */ @@ -460,7 +460,7 @@ bool g_db_collection_unpack(GDbCollection *collec, packed_buffer *pbuf, sqlite3 * * ******************************************************************************/ -bool g_db_collection_pack(GDbCollection *collec, packed_buffer *pbuf, DBAction action, const GDbItem *item) +bool g_db_collection_pack(GDbCollection *collec, packed_buffer_t *pbuf, DBAction action, const GDbItem *item) { bool result; /* Bilan à retourner */ @@ -493,7 +493,7 @@ bool g_db_collection_pack(GDbCollection *collec, packed_buffer *pbuf, DBAction a * * ******************************************************************************/ -bool g_db_collection_pack_all_updates(GDbCollection *collec, packed_buffer *pbuf) +bool g_db_collection_pack_all_updates(GDbCollection *collec, packed_buffer_t *pbuf) { bool result; /* Bilan à renvoyer */ size_t i; /* Boucle de parcours */ @@ -779,7 +779,7 @@ bool g_db_collection_add_item(GDbCollection *collec, GDbItem *item) * * ******************************************************************************/ -bool g_db_collection_drop_disabled_items(GDbCollection *collec, packed_buffer *pbuf) +bool g_db_collection_drop_disabled_items(GDbCollection *collec, packed_buffer_t *pbuf) { bool result; /* Bilan à retourner */ size_t i; /* Boucle de parcours */ @@ -1021,7 +1021,7 @@ static size_t g_db_collection_find_by_timestamp(GDbCollection *collec, timestamp * * ******************************************************************************/ -bool g_db_collection_disable_at(GDbCollection *collec, timestamp_t timestamp, sqlite3 *db, packed_buffer *pbuf) +bool g_db_collection_disable_at(GDbCollection *collec, timestamp_t timestamp, sqlite3 *db, packed_buffer_t *pbuf) { bool result; /* Bilan à retourner */ size_t start; /* Début de la zone à changer */ @@ -1450,7 +1450,7 @@ void lock_unlock_collections(GList *list, bool write, bool lock) * * ******************************************************************************/ -bool pack_all_collection_updates(GList *list, packed_buffer *pbuf) +bool pack_all_collection_updates(GList *list, packed_buffer_t *pbuf) { bool result; /* Bilan à retourner */ GList *iter; /* Boucle de parcours */ @@ -1496,7 +1496,7 @@ bool pack_all_collection_updates(GList *list, packed_buffer *pbuf) * * ******************************************************************************/ -bool update_activity_in_collections(GList *list, packed_buffer *inbuf, packed_buffer *outbuf, sqlite3 *db) +bool update_activity_in_collections(GList *list, packed_buffer_t *inbuf, packed_buffer_t *outbuf, sqlite3 *db) { bool result; /* Résultat global à renvoyer */ bool status; /* Bilan de lecture initiale */ diff --git a/src/analysis/db/collection.h b/src/analysis/db/collection.h index 6c14624..6d7b369 100644 --- a/src/analysis/db/collection.h +++ b/src/analysis/db/collection.h @@ -69,16 +69,16 @@ const char *g_db_collection_get_name(const GDbCollection *); /* Réceptionne un élément depuis une requête réseau. */ -bool _g_db_collection_unpack(GDbCollection *, packed_buffer *, DBAction *, GDbItem **); +bool _g_db_collection_unpack(GDbCollection *, packed_buffer_t *, DBAction *, GDbItem **); /* Réceptionne et traite une requête réseau pour collection. */ -bool g_db_collection_unpack(GDbCollection *, packed_buffer *, sqlite3 *); +bool g_db_collection_unpack(GDbCollection *, packed_buffer_t *, sqlite3 *); /* Envoie pour traitement une requête réseau pour collection. */ -bool g_db_collection_pack(GDbCollection *, packed_buffer *, DBAction, const GDbItem *); +bool g_db_collection_pack(GDbCollection *, packed_buffer_t *, DBAction, const GDbItem *); /* Envoie pour mise à jour tous les éléments courants. */ -bool g_db_collection_pack_all_updates(GDbCollection *, packed_buffer *); +bool g_db_collection_pack_all_updates(GDbCollection *, packed_buffer_t *); @@ -111,13 +111,13 @@ typedef enum _ActiveItemChange bool g_db_collection_add_item(GDbCollection *, GDbItem *); /* Procède au retrait des éléments désactivés de la collection. */ -bool g_db_collection_drop_disabled_items(GDbCollection *, packed_buffer *); +bool g_db_collection_drop_disabled_items(GDbCollection *, packed_buffer_t *); /* Procède au retrait d'un élément dans la collection. */ bool g_db_collection_remove_item(GDbCollection *, const GDbItem *); /* Désactive les éléments en aval d'un horodatage donné. */ -bool g_db_collection_disable_at(GDbCollection *, timestamp_t, sqlite3 *, packed_buffer *); +bool g_db_collection_disable_at(GDbCollection *, timestamp_t, sqlite3 *, packed_buffer_t *); /* Prend acte d'un changement d'état d'un élément de collection. */ bool g_db_collection_update_item_state(GDbCollection *, const GDbItem *); @@ -154,10 +154,10 @@ void lock_unlock_collections(GList *, bool, bool); #define runlock_collections(lst) lock_unlock_collections(lst, false, false); /* Collecte les informations utiles pour un nouvel arrivant. */ -bool pack_all_collection_updates(GList *, packed_buffer *); +bool pack_all_collection_updates(GList *, packed_buffer_t *); /* Met à jour les statuts d'activité des éléments. */ -bool update_activity_in_collections(GList *, packed_buffer *, packed_buffer *, sqlite3 *); +bool update_activity_in_collections(GList *, packed_buffer_t *, packed_buffer_t *, sqlite3 *); diff --git a/src/analysis/db/controller.c b/src/analysis/db/controller.c new file mode 100644 index 0000000..a27c20a --- /dev/null +++ b/src/analysis/db/controller.c @@ -0,0 +1,481 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * controller.h - prototypes pour la gestion d'un ensemble d'archives au format CDB + * + * Copyright (C) 2021 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 "controller.h" + + +#include <assert.h> +#include <errno.h> +#include <dirent.h> +#include <malloc.h> +#include <poll.h> +#include <pthread.h> +#include <string.h> + + +#include <i18n.h> + + +#include "backend-int.h" +#include "misc/rlestr.h" +#include "../../common/leb128.h" +#include "../../common/packed.h" +#include "../../core/logs.h" + + + +/* Description d'un contrôleur d'archives (instance) */ +struct _GCdbController +{ + GServerBackend parent; /* A laisser en premier */ + + char *basedir; /* Répertoire du serveur */ + + SSL *tls_fd; /* Canal de communication */ + char *peer_name; /* Désignation du correspondant*/ + char *user; /* Utilisateur connecté */ + +}; + +/* Description d'un contrôleur d'archives (classe) */ +struct _GCdbControllerClass +{ + GServerBackendClass parent; /* A laisser en premier */ + +}; + + +/* Initialise la classe des contrôleurs d'archives. */ +static void g_cdb_controller_class_init(GCdbControllerClass *); + +/* Initialise un contrôleur d'archives. */ +static void g_cdb_controller_init(GCdbController *); + +/* Supprime toutes les références externes. */ +static void g_cdb_controller_dispose(GCdbController *); + +/* Procède à la libération totale de la mémoire. */ +static void g_cdb_controller_finalize(GCdbController *); + +/* Assure le traitement des requêtes de contrôle. */ +static void *g_cdb_controller_process(GCdbController *); + +/* Prend en compte une connexion nouvelle d'un utilisateur. */ +static void g_cdb_controller_add_client(GCdbController *, SSL *, const char *, const char *); + +/* Envoie au client la liste des binaires présents. */ +static bool g_cdb_controller_send_existing_binaries(GCdbController *, packed_buffer_t *); + + + +/* Indique le type défini pour une gestion d'archives. */ +G_DEFINE_TYPE(GCdbController, g_cdb_controller, G_TYPE_SERVER_BACKEND); + + +/****************************************************************************** +* * +* Paramètres : klass = classe à initialiser. * +* * +* Description : Initialise la classe des contrôleurs d'archives. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_cdb_controller_class_init(GCdbControllerClass *klass) +{ + GObjectClass *object; /* Autre version de la classe */ + GServerBackendClass *backend; /* Classe parente */ + + object = G_OBJECT_CLASS(klass); + + object->dispose = (GObjectFinalizeFunc/* ! */)g_cdb_controller_dispose; + object->finalize = (GObjectFinalizeFunc)g_cdb_controller_finalize; + + backend = G_SERVER_BACKEND_CLASS(klass); + + backend->thread_name = "cdb_controller"; + backend->thread_func = (GThreadFunc)g_cdb_controller_process; + + backend->add_client = (add_backend_client_fc)g_cdb_controller_add_client; + +} + + +/****************************************************************************** +* * +* Paramètres : controller = instance à initialiser. * +* * +* Description : Initialise un contrôleur d'archives. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_cdb_controller_init(GCdbController *controller) +{ + controller->basedir = NULL; + + controller->tls_fd = NULL; + controller->peer_name = NULL; + controller->user = NULL; + +} + + +/****************************************************************************** +* * +* Paramètres : controller = instance d'objet GLib à traiter. * +* * +* Description : Supprime toutes les références externes. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_cdb_controller_dispose(GCdbController *controller) +{ + g_server_backend_stop(G_SERVER_BACKEND(controller)); + + G_OBJECT_CLASS(g_cdb_controller_parent_class)->dispose(G_OBJECT(controller)); + +} + + +/****************************************************************************** +* * +* Paramètres : controller = instance d'objet GLib à traiter. * +* * +* Description : Procède à la libération totale de la mémoire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_cdb_controller_finalize(GCdbController *controller) +{ + if (controller->basedir != NULL) + free(controller->basedir); + + if (controller->tls_fd != NULL) + SSL_free(controller->tls_fd); + + if (controller->peer_name != NULL) + free(controller->peer_name); + + if (controller->user != NULL) + free(controller->user); + + G_OBJECT_CLASS(g_cdb_controller_parent_class)->finalize(G_OBJECT(controller)); + +} + + +/****************************************************************************** +* * +* Paramètres : basedir = répertoire de stockage des enregistrements. * +* error = indication éventuelle en cas d'échec. [OUT] * +* * +* Description : Définit ou ouvre une archive d'éléments utilisateur. * +* * +* Retour : Structure mise en plae ou NULL en cas d'échec. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GCdbController *g_cdb_controller_new(const char *basedir, DBError *error) +{ + GCdbController *result; /* Adresse à retourner */ + + result = g_object_new(G_TYPE_CDB_CONTROLLER, NULL); + + result->basedir = strdup(basedir); + + *error = DBE_NONE; + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : controller = centralisation de tous les savoirs. * +* * +* Description : Assure le traitement des requêtes de contrôle. * +* * +* Retour : NULL. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void *g_cdb_controller_process(GCdbController *controller) +{ + GServerBackend *base; /* Base de l'instance */ + struct pollfd fds[3]; /* Surveillance des flux */ + int ret; /* Bilan d'un appel */ + packed_buffer_t in_pbuf; /* Tampon de réception */ + bool status; /* Bilan de lecture initiale */ + uint32_t command; /* Commande de la requête */ + packed_buffer_t out_pbuf; /* Tampon d'émission */ + char *msg; /* Erreur à faire remonter */ + + base = G_SERVER_BACKEND(controller); + + fds[0].fd = base->stop_ctrl[0]; + fds[0].events = POLLIN | POLLPRI; + + fds[1].fd = base->refresh_ctrl[0]; + fds[1].events = POLLIN | POLLPRI; + + fds[2].fd = SSL_get_fd(controller->tls_fd); + fds[2].events = POLLIN | POLLPRI; + + while (1) + { + /* Lancement d'une phase de surveillance */ + + ret = poll(fds, 3, -1); + if (ret == -1) + { + if (errno == EINTR) continue; + + LOG_ERROR_N("poll"); + break; + + } + + /* Demande expresse d'arrêt des procédures */ + if (fds[0].revents) + break; + + /* Demande d'actualisation ?! */ + assert(fds[1].revents == 0); + + /* Le canal est fermé, une sortie doit être demandée... */ + if (fds[2].revents & POLLNVAL) + goto closed_exchange; + + /** + * Même chose, cf. "TCP: When is EPOLLHUP generated?" + * https://stackoverflow.com/questions/52976152/tcp-when-is-epollhup-generated/52976327#52976327 + */ + + if (fds[2].revents & (POLLHUP | POLLRDHUP)) + goto closed_exchange; + + /* Données présentes en entrée */ + if (fds[2].revents & (POLLIN | POLLPRI)) + { + init_packed_buffer(&in_pbuf); + + status = ssl_recv_packed_buffer(&in_pbuf, controller->tls_fd); + if (!status) goto bad_exchange; + + next_command: + + status = extract_packed_buffer(&in_pbuf, &command, sizeof(uint32_t), true); + if (!status) goto bad_exchange; + + switch (command) + { + case DBC_LIST_BINARIES: + + init_packed_buffer(&out_pbuf); + + status = extend_packed_buffer(&out_pbuf, (uint32_t []) { DBC_EXISTING_BINARIES }, + sizeof(uint32_t), true); + if (!status) goto reply_error; + + status = g_cdb_controller_send_existing_binaries(controller, &out_pbuf); + if (!status) goto reply_error; + + status = ssl_send_packed_buffer(&out_pbuf, controller->tls_fd); + if (!status) goto reply_error; + + exit_packed_buffer(&out_pbuf); + + break; + + default: + asprintf(&msg, _("Bad protocol command: 0x%08x"), command); + LOG_ERROR(LMT_ERROR, msg); + free(msg); + goto bad_exchange; + break; + + } + + if (has_more_data_in_packed_buffer(&in_pbuf)) + goto next_command; + + exit_packed_buffer(&in_pbuf); + + continue; + + reply_error: + + exit_packed_buffer(&out_pbuf); + + bad_exchange: + + LOG_ERROR(LMT_ERROR, _("Bad exchange")); + + assert(0); + + exit_packed_buffer(&in_pbuf); + + closed_exchange: + + break; + + } + + } + + /* On disparaît des écrans... */ + + g_server_backend_stop(G_SERVER_BACKEND(controller)); + + return NULL; + +} + + +/****************************************************************************** +* * +* Paramètres : controller = support pour le suivi d'une connexion. * +* fd = canal de communication réseau ouvert. * +* peer_name = désignation de la connexion. * +* user = désignation de l'utilisateur de la connexion. * +* * +* Description : Prend en compte une connexion nouvelle d'un utilisateur. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_cdb_controller_add_client(GCdbController *controller, SSL *fd, const char *peer_name, const char *user) +{ + controller->tls_fd = fd; + + controller->peer_name = strdup(peer_name); + controller->user = strdup(user); + +} + + +/****************************************************************************** +* * +* Paramètres : controller = administration d'archives d'analyse. * +* pbuf = paquet à consituer pour un envoi unique. [OUT] * +* * +* Description : Envoie au client la liste des binaires présents. * +* * +* Retour : Bilan de constitution de la réponse. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool g_cdb_controller_send_existing_binaries(GCdbController *controller, packed_buffer_t *pbuf) +{ + bool result; /* Bilan à retourner */ + DIR *directory; /* Répertoire de travail */ + uleb128_t count; /* Nombre d'éléments détectés */ + packed_buffer_t items; /* Liste des éléments trouvés */ + struct dirent *item; /* Propriétés d'un élément */ + rle_string name; /* Nom à exporter */ + bool status; /* Bilan d'une inscription */ + int ret; /* Bilan de fermture */ + + result = false; + + directory = opendir(controller->basedir); + if (directory == NULL) + { + LOG_ERROR_N("opendir"); + + if (errno == ENOENT) + { + count = 0; + result = pack_uleb128(&count, pbuf); + } + + goto bad_dir; + + } + + count = 0; + init_packed_buffer(&items); + + for (item = readdir(directory); item != NULL; item = readdir(directory)) + { + if (item->d_type != DT_DIR) + continue; + + if (strcmp(item->d_name, ".") == 0 || strcmp(item->d_name, "..") == 0) + continue; + + init_static_rle_string(&name, item->d_name); + + status = pack_rle_string(&name, &items); + + exit_rle_string(&name); + + if (!status) + goto reg_error; + + count++; + + } + + result = pack_uleb128(&count, pbuf); + + if (result) + result = include_packed_buffer(pbuf, &items); + + reg_error: + + exit_packed_buffer(&items); + + ret = closedir(directory); + if (ret == -1) LOG_ERROR_N("closedir"); + + bad_dir: + + return result; + +} diff --git a/src/analysis/db/controller.h b/src/analysis/db/controller.h new file mode 100644 index 0000000..adeee2b --- /dev/null +++ b/src/analysis/db/controller.h @@ -0,0 +1,59 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * controller.h - prototypes pour la gestion d'un ensemble d'archives au format CDB + * + * Copyright (C) 2014-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/>. + */ + + +#ifndef _ANALYSIS_DB_CONTROLLER_H +#define _ANALYSIS_DB_CONTROLLER_H + + +#include <glib-object.h> +#include <stdbool.h> + + +#include "protocol.h" + + + +#define G_TYPE_CDB_CONTROLLER g_cdb_controller_get_type() +#define G_CDB_CONTROLLER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_CDB_CONTROLLER, GCdbController)) +#define G_IS_CDB_CONTROLLER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_CDB_CONTROLLER)) +#define G_CDB_CONTROLLER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_CDB_CONTROLLER, GCdbControllerClass)) +#define G_IS_CDB_CONTROLLER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_CDB_CONTROLLER)) +#define G_CDB_CONTROLLER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_CDB_CONTROLLER, GCdbControllerClass)) + + +/* Description d'un contrôleur d'archives (instance) */ +typedef struct _GCdbController GCdbController; + +/* Description d'un contrôleur d'archives (classe) */ +typedef struct _GCdbControllerClass GCdbControllerClass; + + +/* Indique le type défini pour une gestion d'archives. */ +GType g_cdb_controller_get_type(void); + +/* Prépare un client pour une connexion à une BD. */ +GCdbController *g_cdb_controller_new(const char *, DBError *); + + + +#endif /* _ANALYSIS_DB_CONTROLLER_H */ diff --git a/src/analysis/db/item-int.h b/src/analysis/db/item-int.h index 4b37844..871a65e 100644 --- a/src/analysis/db/item-int.h +++ b/src/analysis/db/item-int.h @@ -46,10 +46,10 @@ typedef gboolean (* cmp_db_item_key_fc) (const GDbItem *, const GDbItem *); typedef gint (* cmp_db_item_fc) (const GDbItem *, const GDbItem *); /* Importe la définition d'une base d'éléments pour collection. */ -typedef bool (* unpack_db_item_fc) (GDbItem *, packed_buffer *); +typedef bool (* unpack_db_item_fc) (GDbItem *, packed_buffer_t *); /* Exporte la définition d'une base d'éléments pour collection. */ -typedef bool (* pack_db_item_fc) (const GDbItem *, packed_buffer *); +typedef bool (* pack_db_item_fc) (const GDbItem *, packed_buffer_t *); /* Construit la description humaine d'un signet sur un tampon. */ typedef char * (* build_item_label_fc) (const GDbItem *); diff --git a/src/analysis/db/item.c b/src/analysis/db/item.c index 16866ab..6ee59a7 100644 --- a/src/analysis/db/item.c +++ b/src/analysis/db/item.c @@ -49,10 +49,10 @@ static void g_db_item_dispose(GDbItem *); static void g_db_item_finalize(GDbItem *); /* Importe la définition d'une base d'éléments pour collection. */ -static bool _g_db_item_unpack(GDbItem *, packed_buffer *); +static bool _g_db_item_unpack(GDbItem *, packed_buffer_t *); /* Exporte la définition d'une base d'éléments pour collection. */ -static bool _g_db_item_pack(const GDbItem *, packed_buffer *); +static bool _g_db_item_pack(const GDbItem *, packed_buffer_t *); @@ -376,7 +376,7 @@ gint g_db_item_cmp(const GDbItem *a, const GDbItem *b) * * ******************************************************************************/ -static bool _g_db_item_unpack(GDbItem *item, packed_buffer *pbuf) +static bool _g_db_item_unpack(GDbItem *item, packed_buffer_t *pbuf) { bool result; /* Bilan à retourner */ uint32_t flags; /* Propriétés de l'élément */ @@ -410,7 +410,7 @@ static bool _g_db_item_unpack(GDbItem *item, packed_buffer *pbuf) * * ******************************************************************************/ -bool g_db_item_unpack(GDbItem *item, packed_buffer *pbuf) +bool g_db_item_unpack(GDbItem *item, packed_buffer_t *pbuf) { bool result; /* Bilan à retourner */ @@ -434,7 +434,7 @@ bool g_db_item_unpack(GDbItem *item, packed_buffer *pbuf) * * ******************************************************************************/ -static bool _g_db_item_pack(const GDbItem *item, packed_buffer *pbuf) +static bool _g_db_item_pack(const GDbItem *item, packed_buffer_t *pbuf) { bool result; /* Bilan à retourner */ DbItemFlags flags; /* Propriétés de l'élément */ @@ -468,7 +468,7 @@ static bool _g_db_item_pack(const GDbItem *item, packed_buffer *pbuf) * * ******************************************************************************/ -bool g_db_item_pack(const GDbItem *item, packed_buffer *pbuf) +bool g_db_item_pack(const GDbItem *item, packed_buffer_t *pbuf) { return G_DB_ITEM_GET_CLASS(item)->pack(item, pbuf); diff --git a/src/analysis/db/item.h b/src/analysis/db/item.h index a4eaffa..e34523c 100644 --- a/src/analysis/db/item.h +++ b/src/analysis/db/item.h @@ -93,10 +93,10 @@ int g_db_item_cmp_with_timestamp(const timestamp_t *, const GDbItem **); gint g_db_item_cmp(const GDbItem *, const GDbItem *); /* Importe la définition d'une base d'éléments pour collection. */ -bool g_db_item_unpack(GDbItem *, packed_buffer *); +bool g_db_item_unpack(GDbItem *, packed_buffer_t *); /* Exporte la définition d'une base d'éléments pour collection. */ -bool g_db_item_pack(const GDbItem *, packed_buffer *); +bool g_db_item_pack(const GDbItem *, packed_buffer_t *); /* Applique un élément de collection sur un binaire. */ bool g_db_item_apply(GDbItem *, GLoadedBinary *); diff --git a/src/analysis/db/items/Makefile.am b/src/analysis/db/items/Makefile.am index b9ce117..f8f70d5 100644 --- a/src/analysis/db/items/Makefile.am +++ b/src/analysis/db/items/Makefile.am @@ -7,18 +7,9 @@ libanalysisdbitems_la_SOURCES = \ move.h move.c \ switcher.h switcher.c -libanalysisdbitems_la_LIBADD = - -libanalysisdbitems_la_LDFLAGS = +libanalysisdbitems_la_CFLAGS = $(TOOLKIT_CFLAGS) $(LIBXML_CFLAGS) $(LIBSQLITE_CFLAGS) devdir = $(includedir)/chrysalide/$(subdir:src/%=core/%) dev_HEADERS = $(libanalysisdbitems_la_SOURCES:%c=) - - -AM_CPPFLAGS = $(LIBGTK_CFLAGS) $(LIBXML_CFLAGS) $(LIBARCHIVE_CFLAGS) $(LIBSQLITE_CFLAGS) - -AM_CFLAGS = $(DEBUG_CFLAGS) $(WARNING_FLAGS) $(COMPLIANCE_FLAGS) - -SUBDIRS = diff --git a/src/analysis/db/items/bookmark.c b/src/analysis/db/items/bookmark.c index 0a64c89..3610d96 100644 --- a/src/analysis/db/items/bookmark.c +++ b/src/analysis/db/items/bookmark.c @@ -81,10 +81,10 @@ static gboolean g_db_bookmark_cmp_key(const GDbBookmark *, const GDbBookmark *); static gint g_db_bookmark_cmp(const GDbBookmark *, const GDbBookmark *); /* Importe la définition d'un signet dans un flux réseau. */ -static bool g_db_bookmark_unpack(GDbBookmark *, packed_buffer *); +static bool g_db_bookmark_unpack(GDbBookmark *, packed_buffer_t *); /* Exporte la définition d'un signet dans un flux réseau. */ -static bool g_db_bookmark_pack(const GDbBookmark *, packed_buffer *); +static bool g_db_bookmark_pack(const GDbBookmark *, packed_buffer_t *); /* Construit la description humaine d'un signet sur un tampon. */ static char *g_db_bookmark_build_label(const GDbBookmark *); @@ -412,7 +412,7 @@ static gint g_db_bookmark_cmp(const GDbBookmark *a, const GDbBookmark *b) * * ******************************************************************************/ -static bool g_db_bookmark_unpack(GDbBookmark *bookmark, packed_buffer *pbuf) +static bool g_db_bookmark_unpack(GDbBookmark *bookmark, packed_buffer_t *pbuf) { bool result; /* Bilan à retourner */ @@ -442,7 +442,7 @@ static bool g_db_bookmark_unpack(GDbBookmark *bookmark, packed_buffer *pbuf) * * ******************************************************************************/ -static bool g_db_bookmark_pack(const GDbBookmark *bookmark, packed_buffer *pbuf) +static bool g_db_bookmark_pack(const GDbBookmark *bookmark, packed_buffer_t *pbuf) { bool result; /* Bilan à retourner */ diff --git a/src/analysis/db/items/comment.c b/src/analysis/db/items/comment.c index bdff3a6..fb27f60 100644 --- a/src/analysis/db/items/comment.c +++ b/src/analysis/db/items/comment.c @@ -39,9 +39,9 @@ #include "../../human/asm/lang.h" #include "../../../common/array.h" #include "../../../common/extstr.h" +#include "../../../core/columns.h" #include "../../../glibext/gbinarycursor.h" #include "../../../glibext/linegen-int.h" -#include "../../../gtkext/gtkblockdisplay.h" @@ -94,10 +94,10 @@ static gboolean g_db_comment_cmp_key(const GDbComment *, const GDbComment *); static gint g_db_comment_cmp(const GDbComment *, const GDbComment *); /* Importe la définition d'un commentaire dans un flux réseau. */ -static bool g_db_comment_unpack(GDbComment *, packed_buffer *); +static bool g_db_comment_unpack(GDbComment *, packed_buffer_t *); /* Exporte la définition d'un commentaire dans un flux réseau. */ -static bool g_db_comment_pack(GDbComment *, packed_buffer *); +static bool g_db_comment_pack(GDbComment *, packed_buffer_t *); /* Construit la description humaine d'un commentaire. */ static char *g_db_comment_build_label(GDbComment *); @@ -570,7 +570,7 @@ static gint g_db_comment_cmp(const GDbComment *a, const GDbComment *b) * * ******************************************************************************/ -static bool g_db_comment_unpack(GDbComment *comment, packed_buffer *pbuf) +static bool g_db_comment_unpack(GDbComment *comment, packed_buffer_t *pbuf) { bool result; /* Bilan à retourner */ uint8_t tmp8; /* Valeur sur 8 bits */ @@ -623,7 +623,7 @@ static bool g_db_comment_unpack(GDbComment *comment, packed_buffer *pbuf) * * ******************************************************************************/ -static bool g_db_comment_pack(GDbComment *comment, packed_buffer *pbuf) +static bool g_db_comment_pack(GDbComment *comment, packed_buffer_t *pbuf) { bool result; /* Bilan à retourner */ rle_string text; /* Texte brut récupéré */ diff --git a/src/analysis/db/items/move.c b/src/analysis/db/items/move.c index f70de6f..e4f503b 100644 --- a/src/analysis/db/items/move.c +++ b/src/analysis/db/items/move.c @@ -35,9 +35,13 @@ #include "../collection-int.h" #include "../item-int.h" -#include "../../../gui/core/global.h" +#ifdef INCLUDE_GTK_SUPPORT +# include "../../../gui/core/global.h" +#endif #include "../../../glibext/gbinarycursor.h" -#include "../../../glibext/gloadedpanel.h" +#ifdef INCLUDE_GTK_SUPPORT +# include "../../../glibext/gloadedpanel.h" +#endif @@ -78,10 +82,10 @@ static void g_db_move_finalize(GDbMove *); static gint g_db_move_cmp(const GDbMove *, const GDbMove *); /* Importe la définition d'un déplacement depuis un flux réseau. */ -static bool g_db_move_unpack(GDbMove *, packed_buffer *); +static bool g_db_move_unpack(GDbMove *, packed_buffer_t *); /* Exporte la définition d'un déplacement dans un flux réseau. */ -static bool g_db_move_pack(const GDbMove *, packed_buffer *); +static bool g_db_move_pack(const GDbMove *, packed_buffer_t *); /* Construit la description humaine d'un déplacement. */ static char *g_db_move_build_label(GDbMove *); @@ -314,7 +318,7 @@ static gint g_db_move_cmp(const GDbMove *a, const GDbMove *b) * * ******************************************************************************/ -static bool g_db_move_unpack(GDbMove *move, packed_buffer *pbuf) +static bool g_db_move_unpack(GDbMove *move, packed_buffer_t *pbuf) { bool result; /* Bilan à retourner */ @@ -347,7 +351,7 @@ static bool g_db_move_unpack(GDbMove *move, packed_buffer *pbuf) * * ******************************************************************************/ -static bool g_db_move_pack(const GDbMove *move, packed_buffer *pbuf) +static bool g_db_move_pack(const GDbMove *move, packed_buffer_t *pbuf) { bool result; /* Bilan à retourner */ @@ -410,6 +414,8 @@ static char *g_db_move_build_label(GDbMove *move) static bool g_db_move_run(const GDbMove *move, GLineCursor *cursor) { +#ifdef INCLUDE_GTK_SUPPORT + GLoadedPanel *panel; /* Afficheur effectif de code */ typedef struct _move_params @@ -465,6 +471,8 @@ static bool g_db_move_run(const GDbMove *move, GLineCursor *cursor) if (panel != NULL) g_object_unref(G_OBJECT(panel)); +#endif + return true; } diff --git a/src/analysis/db/items/switcher.c b/src/analysis/db/items/switcher.c index 090b8ce..885406c 100644 --- a/src/analysis/db/items/switcher.c +++ b/src/analysis/db/items/switcher.c @@ -84,10 +84,10 @@ static gboolean g_db_switcher_cmp_key(const GDbSwitcher *, const GDbSwitcher *); static gint g_db_switcher_cmp(const GDbSwitcher *, const GDbSwitcher *); /* Importe la définition d'un signet depuis un flux réseau. */ -static bool g_db_switcher_unpack(GDbSwitcher *, packed_buffer *); +static bool g_db_switcher_unpack(GDbSwitcher *, packed_buffer_t *); /* Exporte la définition d'un signet dans un flux réseau. */ -static bool g_db_switcher_pack(const GDbSwitcher *, packed_buffer *); +static bool g_db_switcher_pack(const GDbSwitcher *, packed_buffer_t *); /* Construit la description humaine d'un signet sur un tampon. */ static char *g_db_switcher_build_label(GDbSwitcher *); @@ -448,7 +448,7 @@ static gint g_db_switcher_cmp(const GDbSwitcher *a, const GDbSwitcher *b) * * ******************************************************************************/ -static bool g_db_switcher_unpack(GDbSwitcher *switcher, packed_buffer *pbuf) +static bool g_db_switcher_unpack(GDbSwitcher *switcher, packed_buffer_t *pbuf) { bool result; /* Bilan à retourner */ uint32_t tmp32; /* Valeur sur 32 bits */ @@ -489,7 +489,7 @@ static bool g_db_switcher_unpack(GDbSwitcher *switcher, packed_buffer *pbuf) * * ******************************************************************************/ -static bool g_db_switcher_pack(const GDbSwitcher *switcher, packed_buffer *pbuf) +static bool g_db_switcher_pack(const GDbSwitcher *switcher, packed_buffer_t *pbuf) { bool result; /* Bilan à retourner */ diff --git a/src/analysis/db/misc/Makefile.am b/src/analysis/db/misc/Makefile.am index 6d4af6c..fc3cab2 100644 --- a/src/analysis/db/misc/Makefile.am +++ b/src/analysis/db/misc/Makefile.am @@ -6,18 +6,7 @@ libanalysisdbmisc_la_SOURCES = \ snapshot.h snapshot.c \ timestamp.h timestamp.c -libanalysisdbmisc_la_LIBADD = - -libanalysisdbmisc_la_LDFLAGS = - devdir = $(includedir)/chrysalide/$(subdir:src/%=core/%) dev_HEADERS = $(libanalysisdbmisc_la_SOURCES:%c=) - - -AM_CPPFLAGS = $(LIBGTK_CFLAGS) $(LIBXML_CFLAGS) $(LIBARCHIVE_CFLAGS) $(LIBSQLITE_CFLAGS) - -AM_CFLAGS = $(DEBUG_CFLAGS) $(WARNING_FLAGS) $(COMPLIANCE_FLAGS) - -SUBDIRS = diff --git a/src/analysis/db/misc/rlestr.c b/src/analysis/db/misc/rlestr.c index 7b1e705..a211723 100644 --- a/src/analysis/db/misc/rlestr.c +++ b/src/analysis/db/misc/rlestr.c @@ -29,6 +29,9 @@ #include <string.h> +#include "../../../common/leb128.h" + + /****************************************************************************** * * @@ -273,19 +276,19 @@ int cmp_rle_string(const rle_string *s1, const rle_string *s2) * * ******************************************************************************/ -bool unpack_rle_string(rle_string *str, packed_buffer *pbuf) +bool unpack_rle_string(rle_string *str, packed_buffer_t *pbuf) { bool result; /* Bilan à retourner */ - uint32_t tmp32; /* Valeur sur 32 bits */ + uleb128_t len; /* Quantité de caractères */ unset_rle_string(str); - result = extract_packed_buffer(pbuf, &tmp32, sizeof(uint32_t), true); - - str->length = tmp32; + result = unpack_uleb128(&len, pbuf); - if (result && str->length > 0) + if (result && len > 0) { + str->length = len; + str->data = malloc(str->length + 1); str->dynamic = true; @@ -317,11 +320,11 @@ bool unpack_rle_string(rle_string *str, packed_buffer *pbuf) * * ******************************************************************************/ -bool pack_rle_string(const rle_string *str, packed_buffer *pbuf) +bool pack_rle_string(const rle_string *str, packed_buffer_t *pbuf) { bool result; /* Bilan à retourner */ - result = extend_packed_buffer(pbuf, (uint32_t []) { str->length }, sizeof(uint32_t), true); + result = pack_uleb128((uleb128_t []){ str->length }, pbuf); if (result && str->length > 0) result = extend_packed_buffer(pbuf, str->data, str->length + 1, false); diff --git a/src/analysis/db/misc/rlestr.h b/src/analysis/db/misc/rlestr.h index 63effda..aa2aa73 100644 --- a/src/analysis/db/misc/rlestr.h +++ b/src/analysis/db/misc/rlestr.h @@ -83,10 +83,10 @@ void unset_rle_string(rle_string *); int cmp_rle_string(const rle_string *, const rle_string *); /* Importe la définition d'une chaîne de caractères. */ -bool unpack_rle_string(rle_string *, packed_buffer *); +bool unpack_rle_string(rle_string *, packed_buffer_t *); /* Exporte la définition d'une chaîne de caractères. */ -bool pack_rle_string(const rle_string *, packed_buffer *); +bool pack_rle_string(const rle_string *, packed_buffer_t *); diff --git a/src/analysis/db/misc/snapshot.c b/src/analysis/db/misc/snapshot.c index 23fe15c..b116b3c 100644 --- a/src/analysis/db/misc/snapshot.c +++ b/src/analysis/db/misc/snapshot.c @@ -188,7 +188,7 @@ int cmp_snapshot_id(const snapshot_id_t *id1, const snapshot_id_t *id2) * * ******************************************************************************/ -bool unpack_snapshot_id(snapshot_id_t *id, packed_buffer *pbuf) +bool unpack_snapshot_id(snapshot_id_t *id, packed_buffer_t *pbuf) { bool result; /* Bilan à retourner */ @@ -212,7 +212,7 @@ bool unpack_snapshot_id(snapshot_id_t *id, packed_buffer *pbuf) * * ******************************************************************************/ -bool pack_snapshot_id(const snapshot_id_t *id, packed_buffer *pbuf) +bool pack_snapshot_id(const snapshot_id_t *id, packed_buffer_t *pbuf) { bool result; /* Bilan à retourner */ @@ -411,7 +411,7 @@ void copy_snapshot_info(snapshot_info_t *dest, const snapshot_info_t *src) * * ******************************************************************************/ -bool unpack_snapshot_info(snapshot_info_t *info, packed_buffer *pbuf) +bool unpack_snapshot_info(snapshot_info_t *info, packed_buffer_t *pbuf) { bool result; /* Bilan à retourner */ rle_string string; /* Chaîne à transmettre */ @@ -473,7 +473,7 @@ bool unpack_snapshot_info(snapshot_info_t *info, packed_buffer *pbuf) * * ******************************************************************************/ -bool pack_snapshot_info(const snapshot_info_t *info, packed_buffer *pbuf) +bool pack_snapshot_info(const snapshot_info_t *info, packed_buffer_t *pbuf) { bool result; /* Bilan à retourner */ rle_string string; /* Chaîne à transmettre */ diff --git a/src/analysis/db/misc/snapshot.h b/src/analysis/db/misc/snapshot.h index 3f55b50..f6a84f1 100644 --- a/src/analysis/db/misc/snapshot.h +++ b/src/analysis/db/misc/snapshot.h @@ -71,10 +71,10 @@ void copy_snapshot_id(snapshot_id_t *, const snapshot_id_t *); int cmp_snapshot_id(const snapshot_id_t *, const snapshot_id_t *); /* Importe la définition d'un identifiant d'instantané. */ -bool unpack_snapshot_id(snapshot_id_t *, packed_buffer *); +bool unpack_snapshot_id(snapshot_id_t *, packed_buffer_t *); /* Exporte la définition d'un identifiant d'instantané. */ -bool pack_snapshot_id(const snapshot_id_t *, packed_buffer *); +bool pack_snapshot_id(const snapshot_id_t *, packed_buffer_t *); @@ -117,10 +117,10 @@ void exit_snapshot_info(snapshot_info_t *); void copy_snapshot_info(snapshot_info_t *, const snapshot_info_t *); /* Importe la description d'un identifiant d'instantané. */ -bool unpack_snapshot_info(snapshot_info_t *, packed_buffer *); +bool unpack_snapshot_info(snapshot_info_t *, packed_buffer_t *); /* Exporte la description d'un identifiant d'instantané. */ -bool pack_snapshot_info(const snapshot_info_t *, packed_buffer *); +bool pack_snapshot_info(const snapshot_info_t *, packed_buffer_t *); /* Change la désignation dans les informations d'un instantané. */ void set_snapshot_info_name(snapshot_info_t *, const char *); diff --git a/src/analysis/db/misc/timestamp.c b/src/analysis/db/misc/timestamp.c index 5f49997..1790e2b 100644 --- a/src/analysis/db/misc/timestamp.c +++ b/src/analysis/db/misc/timestamp.c @@ -203,7 +203,7 @@ int cmp_timestamp(const timestamp_t *t1, const timestamp_t *t2) * * ******************************************************************************/ -bool unpack_timestamp(timestamp_t *timestamp, packed_buffer *pbuf) +bool unpack_timestamp(timestamp_t *timestamp, packed_buffer_t *pbuf) { bool result; /* Bilan à retourner */ @@ -227,7 +227,7 @@ bool unpack_timestamp(timestamp_t *timestamp, packed_buffer *pbuf) * * ******************************************************************************/ -bool pack_timestamp(const timestamp_t *timestamp, packed_buffer *pbuf) +bool pack_timestamp(const timestamp_t *timestamp, packed_buffer_t *pbuf) { bool result; /* Bilan à retourner */ diff --git a/src/analysis/db/misc/timestamp.h b/src/analysis/db/misc/timestamp.h index f41a529..bdb9d3e 100644 --- a/src/analysis/db/misc/timestamp.h +++ b/src/analysis/db/misc/timestamp.h @@ -57,10 +57,10 @@ void copy_timestamp(timestamp_t *, const timestamp_t *); int cmp_timestamp(const timestamp_t *, const timestamp_t *); /* Importe la définition d'un horodatage. */ -bool unpack_timestamp(timestamp_t *, packed_buffer *); +bool unpack_timestamp(timestamp_t *, packed_buffer_t *); /* Exporte la définition d'un horodatage. */ -bool pack_timestamp(const timestamp_t *, packed_buffer *); +bool pack_timestamp(const timestamp_t *, packed_buffer_t *); diff --git a/src/analysis/db/protocol.h b/src/analysis/db/protocol.h index f673c4b..7707058 100644 --- a/src/analysis/db/protocol.h +++ b/src/analysis/db/protocol.h @@ -29,8 +29,12 @@ /** * Version de la définition courante du protocole. */ -#define CDB_PROTOCOL_VERSION 0xc0de0004 +#define CDB_PROTOCOL_VERSION 0xc0de0005 +/** + * 0xc0de0005 : + * - création des rôles d'aministrateur et d'analyste + */ @@ -43,34 +47,58 @@ -/* Comportement vis à vis des éléments */ -typedef enum _DBStorage -{ - DBS_ALL_LOCAL = 0x00, /* Enregistrements locaux */ - DBS_ALL_REMOTE = 0x01, /* Enregistrements distants */ - DBS_MAX = 0x01 - -} DBStorage; +/* Rôle à envoyer lors des présentations */ +typedef enum _ClientRole +{ + CRL_UNDEFINED = 0, /* Rôle non défini */ + CRL_ADMIN = 1, /* Rôle d'administrateur */ + CRL_ANALYST = 2, /* Rôle d'analyste */ +} ClientRole; +/* Niveaux de privilèges */ +typedef enum _ServerPrivLevels +{ + SPV_UNDEFINED = 0, /* Rôle non défini */ + SPV_ADMINISTRATOR = 1, /* Pleins pouvoirs */ + SPV_MANAGER = 2, /* Gestionnaire de comptes */ + SPV_CREATOR = 3, /* Gestionnaire d'analyses */ + SPV_ANALYST = 4, /* Analyste de binaires */ +} ServerPrivLevels; /** - * Une fois la connexion établie, les paquets ont tous la forme suivante : + * Précisions pour la commande DBC_LOADING_STATUS. + */ +/* Eléments de base nécessaires */ +typedef enum _LoadingStatusHint +{ + LSH_READY = 0, /* (Plus) rien n'est requis */ + LSH_ON_WAIT_LIST = 1, /* Concurrence des connexions */ + LSH_NEED_CONTENT = 2, /* Suppléments nécessaires */ + LSH_NEED_FORMAT = 3, /* Suppléments nécessaires */ + LSH_NEED_ARCH = 4, /* Suppléments nécessaires */ +} LoadingStatusHint; + + + + +/** + * Une fois la connexion établie, les paquets ont tous la forme suivante : * * [ type de collection visée ; cf. DBFeatures ] * [ action à mener ; cf. DBAction ] @@ -114,9 +142,91 @@ typedef enum _DBAction */ typedef enum _DBCommand { + /* ------------------------- Commandes à portée générale ------------------------- */ + + /** + * Le client envoie un tout premier paquet de la forme suivante : + * + * [ Ordre de sauvegarde : DBC_HELO ] + * [ Protocole supporté : CDB_PROTOCOL_VERSION ] + * [ Rôle visé ; cf ClientRole ] + * [ Compléments selon le rôle visé ] + * + * Le serveur effectue les validations et renvoie un bilan : + * + * [ Ordre de sauvegarde : DBC_WELCOME ] + * [ Statut d'exécution ; cf. DBError ] + * + */ + DBC_HELO, /* Connexion initiale C -> S */ DBC_WELCOME, /* Réponse initiale S -> C */ + + /* ------------------------ Commandes pour administrateur ------------------------ */ + + /** + * Le client envoie une requête pour lister les binaires de la forme suivante : + * + * [ Demande de liste : DBC_LIST_BINARIES ] + * + * Le serveur liste tous les répertoires présents et renvoie cette liste : + * + * [ Marqueur de liste : DBC_EXISTING_BINARIES ] + * [ Quantité d'éléments en ULEB128 ] + * [ Noms en chaîne RLE... ] + * + */ + + DBC_LIST_BINARIES, /* Fourniture des identifiants */ + DBC_EXISTING_BINARIES, /* Eléments présents */ + + + /* ------------------------ Commandes pour analyste ------------------------ */ + + /** + * Gestion de la commande 'DBC_LOADING_STATUS'. + * + * Le serveur envoie un statut de prise en charge au début d'une connexion : + * + * [ Indication du serveur : DBC_LOADING_STATUS] + * [ Statut courant ; cf. LoadingStatusHint ] + * + */ + + DBC_LOADING_STATUS, /* Indications initiales */ + + /** + * Gestion de la commande 'DBC_SET_CONTENT'. + * + * Le client connecté envoie un paquet de la forme suivante : + * + * [ Ordre de sauvegarde : DBC_SET_CONTENT ] + * [ Quantité des données suivantes ] + * [ Position du contenu + données de stockage ] + * + * Le serveur s'exécute et renvoie un bilan : + * + * [ Ordre de sauvegarde : DBC_SET_CONTENT ] + * [ Statut d'exécution ; cf. DBError ] + * + */ + + DBC_SET_CONTENT, + + + + + + + + + + + + + + /** * Gestion de la commande 'DBC_SAVE'. * @@ -329,6 +439,7 @@ typedef enum _DBError DBE_XML_VERSION_ERROR, /* Vieille archive présente */ DBE_DB_LOADING_ERROR, /* Erreur pendant le chargement*/ + DBE_WRONG_HASH, /* Empreinte inattendue */ DBE_XML_ERROR, /* Erreur lors d'une définition*/ DBE_SNAPSHOT_NOT_FOUND, /* Instantané non trouvé */ DBE_SNAPSHOT_RESTORE_FAILURE, /* Echec d'une restauration */ diff --git a/src/analysis/db/server.c b/src/analysis/db/server.c index 258a66c..79d5df1 100644 --- a/src/analysis/db/server.c +++ b/src/analysis/db/server.c @@ -42,7 +42,9 @@ #include "auth.h" +#include "backend.h" #include "cdb.h" +#include "controller.h" #include "protocol.h" #include "misc/rlestr.h" #include "../../common/extstr.h" @@ -92,8 +94,10 @@ struct _GHubServer GThread *listener; /* Procédure de traitement */ - GList *archives; /* Liste des binaires ouverts */ - GMutex mutex; /* Verrou pour l'accès */ + GList *controllers; /* Liste des administrateurs */ + GMutex ctrl_mutex; /* Verrou pour l'accès */ + GList *archives; /* Liste des aanlystes */ + GMutex ar_mutex; /* Verrou pour l'accès */ GMutex wait_mutex; /* Accès à la condition */ GCond wait_cond; /* Attente de signal */ @@ -136,6 +140,18 @@ static int g_hub_server_verify(int, X509_STORE_CTX *); /* Assure l'accueil des nouveaux clients. */ static void *g_hub_server_listener(GHubServer *); +/* Assure l'accueil des nouveaux clients administrateurs. */ +static GServerBackend *g_hub_server_handle_admin(GHubServer *, packed_buffer_t *, const char *, DBError *, bool *); + +/* Assure l'accueil des nouveaux clients analystes. */ +static GServerBackend *g_hub_server_handle_analyst(GHubServer *, packed_buffer_t *, const char *, DBError *, bool *); + +/* Enregistre dans une liste interne un support de suivi. */ +static void g_hub_server_register_backend(GHubServer *, GServerBackend *); + +/* Suit les variations du compteur de références d'un greffon. */ +static void on_backend_ref_toggle(GHubServer *, GServerBackend *, gboolean); + /* Indique le type défini pour une description de serveur à l'écoute. */ @@ -190,7 +206,10 @@ static void g_hub_server_init(GHubServer *server) server->unlock_socket = NULL; server->lock_fd = -1; - g_mutex_init(&server->mutex); + server->controllers = NULL; + g_mutex_init(&server->ctrl_mutex); + server->archives = NULL; + g_mutex_init(&server->ar_mutex); g_mutex_init(&server->wait_mutex); g_cond_init(&server->wait_cond); @@ -216,15 +235,23 @@ static void g_hub_server_dispose(GHubServer *server) g_hub_server_stop(server); + for (iter = g_list_first(server->controllers); + iter != NULL; + iter = g_list_first(server->controllers)) + { + g_object_unref(G_OBJECT(iter->data)); + } + + g_mutex_clear(&server->ctrl_mutex); + for (iter = g_list_first(server->archives); iter != NULL; iter = g_list_first(server->archives)) { g_object_unref(G_OBJECT(iter->data)); - server->archives = g_list_delete_link(server->archives, iter); } - g_mutex_clear(&server->mutex); + g_mutex_clear(&server->ar_mutex); g_mutex_clear(&server->wait_mutex); g_cond_clear(&server->wait_cond); @@ -721,19 +748,17 @@ static void *g_hub_server_listener(GHubServer *server) gen_sockaddr_t peer; /* Adresse cliente */ int fd; /* Canal établi vers un client */ SSL *tls_fd; /* Même canal, mais sécurisé */ - rle_string hash; /* Empreinte du binaire visé */ + GServerBackend *backend; /* Support de suivi créé */ const char *ip; /* Statut de la conversion */ char *peer_name; /* Désignation du correspondant*/ DBError error; /* Validation de la connexion */ - GCdbArchive *archive; /* Destinataire final du client*/ - GList *iter; /* Boucle de parcours */ - packed_buffer in_pbuf; /* Tampon de réception */ + packed_buffer_t in_pbuf; /* Tampon de réception */ bool status; /* Bilan d'une opération */ uint32_t cmd; /* Commande initiale lue */ uint32_t version; /* Version du client lue */ - char *basedir; /* Répertoire de stockage */ - char *tmpdir; /* Répertoire de travail */ - packed_buffer out_pbuf; /* Tampon d'émission */ + uint32_t role; /* Rôle visé par le client */ + bool new; /* Besoin d'ajout à une liste */ + packed_buffer_t out_pbuf; /* Tampon d'émission */ fds.fd = server->fd; fds.events = POLLIN | POLLPRI; @@ -782,12 +807,10 @@ static void *g_hub_server_listener(GHubServer *server) goto invalid_conn; } - /* Initialisation à vide pour les sorties en erreur */ - - setup_empty_rle_string(&hash); - /* Construction d'une représentation */ + backend = NULL; + if (*((sa_family_t *)&peer) == AF_UNIX) peer_name = strdup(server->desc); @@ -799,7 +822,7 @@ static void *g_hub_server_listener(GHubServer *server) if (ip == NULL) { LOG_ERROR_N("inet_ntop"); - goto id_error; + goto ip_error; } snprintf(peer_name + strlen(ip), 1 + 5, ":%hu", ntohs(peer.inet4_addr.sin_port)); @@ -814,7 +837,7 @@ static void *g_hub_server_listener(GHubServer *server) if (ip == NULL) { LOG_ERROR_N("inet_ntop"); - goto id_error; + goto ip_error; } snprintf(peer_name + strlen(ip), 1 + 5, ":%hu", ntohs(peer.inet6_addr.sin6_port)); @@ -825,14 +848,12 @@ static void *g_hub_server_listener(GHubServer *server) goto invalid_conn; error = DBE_NONE; - archive = NULL; - - iter = NULL; /** * Le premier "paquet" reçu de la part d'un client doit contenir les informations suivantes : - * - la commande 'DBC_HELO'. - * - le numéro de version du client. + * - la commande 'DBC_HELO' ; + * - le numéro de version du client ; + * - le rôle attendu. * - l'empreinte du binaire analysé. * * Tout ceci est à synchroniser avec la fonction g_db_client_start(). @@ -846,7 +867,7 @@ static void *g_hub_server_listener(GHubServer *server) log_variadic_message(LMT_ERROR, _("Error while getting the initial packet from '%s'..."), peer_name); error = DBE_BAD_EXCHANGE; - goto error_sending; + goto error_receiving; } status = extract_packed_buffer(&in_pbuf, &cmd, sizeof(uint32_t), true); @@ -855,7 +876,7 @@ static void *g_hub_server_listener(GHubServer *server) log_variadic_message(LMT_ERROR, _("Error while getting the initial command from '%s'..."), peer_name); error = DBE_BAD_EXCHANGE; - goto error_sending; + goto error_receiving; } status = extract_packed_buffer(&in_pbuf, &version, sizeof(uint32_t), true); @@ -864,16 +885,16 @@ static void *g_hub_server_listener(GHubServer *server) log_variadic_message(LMT_ERROR, _("Error while getting the protocol version from '%s'..."), peer_name); error = DBE_BAD_EXCHANGE; - goto error_sending; + goto error_receiving; } - status = unpack_rle_string(&hash, &in_pbuf); + status = extract_packed_buffer(&in_pbuf, &role, sizeof(uint32_t), true); if (!status) { - log_variadic_message(LMT_ERROR, _("Error while getting the binary hash from '%s'..."), + log_variadic_message(LMT_ERROR, _("Error while getting the expected role from '%s'..."), peer_name); error = DBE_BAD_EXCHANGE; - goto error_sending; + goto error_receiving; } if (cmd != DBC_HELO) @@ -881,65 +902,48 @@ static void *g_hub_server_listener(GHubServer *server) log_variadic_message(LMT_ERROR, _("The client from '%s' did not introduce itself!"), peer_name); error = DBE_BAD_EXCHANGE; - goto error_sending; + goto error_receiving; } if (version != CDB_PROTOCOL_VERSION) { - log_variadic_message(LMT_ERROR, _("The client from '%s' does not use the same protocol: 0x%08x vs 0x%08x..."), + log_variadic_message(LMT_ERROR, + _("The client from '%s' does not use the same protocol: 0x%08x vs 0x%08x..."), peer_name, be32toh(version), CDB_PROTOCOL_VERSION); error = DBE_WRONG_VERSION; - goto error_sending; + goto error_receiving; } - if (is_rle_string_empty(&hash)) + switch (role) { - log_variadic_message(LMT_ERROR, _("The submitted binary hash from '%s' is empty!"), - peer_name); - error = DBE_BAD_EXCHANGE; - goto error_sending; - } - - /** - * On met en place le maximum ici, de manière à pouvoir indiquer une erreur - * en cas d'échec, et être le plus précis possible dans la courte réponse. - */ - - assert(error == DBE_NONE); + case CRL_ADMIN: + backend = g_hub_server_handle_admin(server, &in_pbuf, peer_name, &error, &new); + break; - for (iter = g_list_first(server->archives); - iter != NULL; - iter = g_list_next(iter)) - { - archive = G_CDB_ARCHIVE(iter->data); - if (g_cdb_archive_compare_hash(archive, &hash) == 0) + case CRL_ANALYST: + backend = g_hub_server_handle_analyst(server, &in_pbuf, peer_name, &error, &new); break; - } - if (iter == NULL) - { - basedir = strdup(server->working); - basedir = stradd(basedir, "cdbs" G_DIR_SEPARATOR_S); + default: + log_variadic_message(LMT_ERROR, _("Unknown client role 0x%x requested by '%s'"), + role, peer_name); + backend = NULL; + error = DBE_BAD_EXCHANGE; + new = false; + break; - tmpdir = strdup(server->working); - tmpdir = stradd(tmpdir, "tmp" G_DIR_SEPARATOR_S); + } - archive = g_cdb_archive_new(basedir, tmpdir, &hash, &error); + assert((backend == NULL && error != DBE_NONE) || (backend != NULL && error == DBE_NONE)); - free(tmpdir); - free(basedir); - - } + error_receiving: /** * Le serveur doit répondre pour un message type : - * - la commande 'DBC_WELCOME'. - * - un identifiant d'erreur ('DBE_NONE', 'DBE_BAD_EXCHANGE' - * ou 'DBE_WRONG_VERSION' ... 'DBE_LOADING_ERROR'). + * - la commande 'DBC_WELCOME' ; + * - un identifiant d'erreur. */ - error_sending: - exit_packed_buffer(&in_pbuf); init_packed_buffer(&out_pbuf); @@ -959,41 +963,28 @@ static void *g_hub_server_listener(GHubServer *server) * lors des échanges initiaux, car ces derniers seraient alors précédés des mises à jour... */ - if (archive != NULL) + if (backend != NULL) { - assert(error == DBE_NONE); - - /* Si l'archive a été créée pour l'occasion... */ - if (iter == NULL) - server->archives = g_list_append(server->archives, archive); - - g_cdb_archive_add_client(archive, tls_fd); - - exit_packed_buffer(&out_pbuf); - - free(peer_name); + if (new) + g_hub_server_register_backend(server, backend); - exit_rle_string(&hash); - - continue; + g_server_backend_add_client(backend, tls_fd, peer_name); } - assert(error != DBE_NONE); - out_error: - exit_packed_buffer(&out_pbuf); + if (backend != NULL) + g_object_unref(G_OBJECT(backend)); - /* Si l'archive a été créée pour l'occasion... */ - if (iter == NULL && archive != NULL) - g_object_unref(G_OBJECT(archive)); + exit_packed_buffer(&out_pbuf); - id_error: + ip_error: free(peer_name); - exit_rle_string(&hash); + if (backend != NULL) + continue; invalid_conn: @@ -1016,6 +1007,283 @@ static void *g_hub_server_listener(GHubServer *server) /****************************************************************************** * * +* Paramètres : server = serveur pour les accès distants à manipuler. * +* in_pbuf = reste des premières données reçues. * +* peer_name = désignation de la connexion entrante. * +* error = code d'erreur issu du traitement. [OUT] * +* new = indique si le résultat doit être ajouté. [OUT] * +* * +* Description : Assure l'accueil des nouveaux clients administrateurs. * +* * +* Retour : Instance de support de suivi mise en place. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static GServerBackend *g_hub_server_handle_admin(GHubServer *server, packed_buffer_t *in_pbuf, const char *peer_name, DBError *error, bool *new) +{ + GCdbController *result; /* Support de suivi à retourner*/ + char *basedir; /* Répertoire de stockage */ + + if (has_more_data_in_packed_buffer(in_pbuf)) + { + log_variadic_message(LMT_ERROR, _("The client from '%s' provided too much data!"), peer_name); + + result = NULL; + + *error = DBE_BAD_EXCHANGE; + *new = false; + + } + else + { + basedir = strdup(server->working); + basedir = stradd(basedir, "cdbs" G_DIR_SEPARATOR_S); + + result = g_cdb_controller_new(basedir, error); + + free(basedir); + + *new = true; + + } + + return G_SERVER_BACKEND(result); + +} + + +/****************************************************************************** +* * +* Paramètres : server = serveur pour les accès distants à manipuler. * +* in_pbuf = reste des premières données reçues. * +* peer_name = désignation de la connexion entrante. * +* error = code d'erreur issu du traitement. [OUT] * +* new = indique si le résultat doit être ajouté. [OUT] * +* * +* Description : Assure l'accueil des nouveaux clients analystes. * +* * +* Retour : Instance de support de suivi mise en place. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static GServerBackend *g_hub_server_handle_analyst(GHubServer *server, packed_buffer_t *in_pbuf, const char *peer_name, DBError *error, bool *new) +{ + GCdbArchive *result; /* Support de suivi à retourner*/ + rle_string hash; /* Empreinte du binaire visé */ + rle_string class; /* Nature du contenu visé */ + bool status; /* Bilan d'une opération */ + GList *iter; /* Boucle de parcours */ + GCdbArchive *archive; /* Destinataire final du client*/ + char *basedir; /* Répertoire de stockage */ + char *tmpdir; /* Répertoire de travail */ + + result = NULL; + + *error = DBE_BAD_EXCHANGE; + *new = false; + + /* Fin de réception des données envoyées */ + + setup_empty_rle_string(&hash); + + status = unpack_rle_string(&hash, in_pbuf); + if (!status) + { + log_variadic_message(LMT_ERROR, _("Error while getting the binary hash from '%s'..."), peer_name); + goto error_receiving; + } + + if (is_rle_string_empty(&hash)) + { + log_variadic_message(LMT_ERROR, _("The submitted binary hash from '%s' is empty!"), peer_name); + goto wrong_receiving_0; + } + + setup_empty_rle_string(&class); + + status = unpack_rle_string(&class, in_pbuf); + if (!status) + { + log_variadic_message(LMT_ERROR, _("Error while getting the content class from '%s'..."), peer_name); + goto wrong_receiving_0; + } + + if (is_rle_string_empty(&class)) + { + log_variadic_message(LMT_ERROR, _("The submitted content class from '%s' is empty!"), peer_name); + goto wrong_receiving_1; + } + + if (has_more_data_in_packed_buffer(in_pbuf)) + { + log_variadic_message(LMT_ERROR, _("The client from '%s' provided too much data!"), peer_name); + goto wrong_receiving_1; + } + + /* Recherche d'un support existant adapté */ + + g_mutex_lock(&server->ar_mutex); + + for (iter = g_list_first(server->archives); iter != NULL; iter = g_list_next(iter)) + { + archive = G_CDB_ARCHIVE(iter->data); + + if (g_cdb_archive_compare_is_suitable_for(archive, &hash, &class)) + break; + + } + + if (iter != NULL) + { + result = archive; + g_object_ref(G_OBJECT(result)); + } + + g_mutex_unlock(&server->ar_mutex); + + /* Nouvelle création au besoin */ + + if (result == NULL) + { + basedir = strdup(server->working); + basedir = stradd(basedir, "cdbs" G_DIR_SEPARATOR_S); + + tmpdir = strdup(server->working); + tmpdir = stradd(tmpdir, "tmp" G_DIR_SEPARATOR_S); + + result = g_cdb_archive_new(basedir, tmpdir, &hash, &class, error); + + free(tmpdir); + free(basedir); + + *new = true; + + } + + wrong_receiving_1: + + exit_rle_string(&class); + + wrong_receiving_0: + + exit_rle_string(&hash); + + error_receiving: + + return (result != NULL ? G_SERVER_BACKEND(result) : NULL); + +} + + +/****************************************************************************** +* * +* Paramètres : server = serveur pour les accès distants à manipuler. * +* backend = support de suivi de connexion. * +* * +* Description : Enregistre dans une liste interne un support de suivi. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_hub_server_register_backend(GHubServer *server, GServerBackend *backend) +{ + GList **list; /* Liste à parcourir */ + GMutex *mutex; /* Verrou à manipuler */ + + /* Sélection des éléments concernés */ + + if (G_IS_CDB_CONTROLLER(backend)) + { + list = &server->controllers; + mutex = &server->ctrl_mutex; + } + else if (G_IS_CDB_ARCHIVE(backend)) + { + list = &server->archives; + mutex = &server->ar_mutex; + } + else + assert(false); + + /* Retrait de l'élément inutilisé */ + + g_mutex_lock(mutex); + + g_object_ref(G_OBJECT(backend)); + + *list = g_list_append(*list, backend); + + g_object_add_toggle_ref(G_OBJECT(backend), (GToggleNotify)on_backend_ref_toggle, server); + + g_mutex_unlock(mutex); + +} + + +/****************************************************************************** +* * +* Paramètres : server = serveur pour les accès distants à manipuler. * +* backend = support de suivi de connexion. * +* last = indication sur la valeur du compteur de références.* +* * +* Description : Suit les variations du compteur de références d'un greffon. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void on_backend_ref_toggle(GHubServer *server, GServerBackend *backend, gboolean last) +{ + GList **list; /* Liste à parcourir */ + GMutex *mutex; /* Verrou à manipuler */ + GList *iter; /* Boucle de parcours */ + + if (last) + { + /* Sélection des éléments concernés */ + + if (G_IS_CDB_CONTROLLER(backend)) + { + list = &server->controllers; + mutex = &server->ctrl_mutex; + } + else if (G_IS_CDB_ARCHIVE(backend)) + { + list = &server->archives; + mutex = &server->ar_mutex; + } + else + assert(false); + + /* Retrait de l'élément inutilisé */ + + g_mutex_lock(mutex); + + for (iter = g_list_first(*list); iter != NULL; iter = g_list_first(*list)) + { + *list = g_list_delete_link(*list, iter); + } + + g_object_remove_toggle_ref(G_OBJECT(backend), (GToggleNotify)on_backend_ref_toggle, server); + + g_mutex_unlock(mutex); + + } + +} + + +/****************************************************************************** +* * * Paramètres : server = serveur pour les accès distants à manipuler. * * backlog = nombre de connexions maximal. * * keep = conservation du serveur en avant plan. * diff --git a/src/analysis/db/snapshot.c b/src/analysis/db/snapshot.c index e07129e..170cab5 100644 --- a/src/analysis/db/snapshot.c +++ b/src/analysis/db/snapshot.c @@ -37,7 +37,6 @@ #include "collection.h" #include "../../common/compression.h" #include "../../common/extstr.h" -#include "../../common/io.h" #include "../../common/sqlite.h" #include "../../common/xml.h" #include "../../core/logs.h" @@ -69,7 +68,7 @@ static snapshot_node_t *create_snapshot_node(const char *, uint64_t, const char 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 *); +static bool setup_snapshot_node_db_path(snapshot_node_t *, const GCdbArchive *); /* Valide la présence d'une base de données pour chaque noeud. */ static bool check_snapshot_nodes(const snapshot_node_t *); @@ -90,7 +89,7 @@ static void add_snapshot_node(snapshot_node_t *, snapshot_node_t *); static void remove_snapshot_node(snapshot_node_t *, bool); /* Collecte les descriptions d'une arborescence d'instantanés. */ -static bool pack_snapshot_node(const snapshot_node_t *, packed_buffer *); +static bool pack_snapshot_node(const snapshot_node_t *, packed_buffer_t *); @@ -102,9 +101,6 @@ 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 */ @@ -133,7 +129,7 @@ static void g_db_snapshot_dispose(GDbSnapshot *); 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 *); +static GDbSnapshot *g_db_snapshot_new(const GCdbArchive *); @@ -231,9 +227,8 @@ static void destroy_snapshot_node(snapshot_node_t *node) /****************************************************************************** * * -* Paramètres : node = noeud d'instantané à traiter. * -* tmpdir = répertoire de travail temporaire. * -* hash = empreinte du binaire à représenter. * +* Paramètres : node = noeud d'instantané à traiter. * +* archive = archive concernée par l'instantané. * * * * Description : Définit le chemin vers une base de données pour un noeud. * * * @@ -243,23 +238,26 @@ static void destroy_snapshot_node(snapshot_node_t *node) * * ******************************************************************************/ -static bool setup_snapshot_node_db_path(snapshot_node_t *node, const char *tmpdir, const char *hash) +static bool setup_snapshot_node_db_path(snapshot_node_t *node, const GCdbArchive *archive) { bool result; /* Bilan à retourner */ snapshot_id_t *id; /* Identifiant attribué */ + char *suffix; /* Fin du fichier temporaire */ int ret; /* Bilan d'une génération */ id = get_snapshot_info_id(&node->info); - ret = asprintf(&node->path, "%s" G_DIR_SEPARATOR_S "%s_%s_db.sql", - tmpdir, hash, snapshot_id_as_string(id)); - + ret = asprintf(&suffix, "%s_db.sql", snapshot_id_as_string(id)); result = (ret > 0); if (result) { - ret = ensure_path_exists(node->path); - result = (ret != -1); + node->path = g_cdb_archive_get_tmp_filename(archive, suffix); + + free(suffix); + + result = (node->path != NULL); + } return result; @@ -639,7 +637,7 @@ static void remove_snapshot_node(snapshot_node_t *node, bool rec) * * ******************************************************************************/ -static bool pack_snapshot_node(const snapshot_node_t *node, packed_buffer *pbuf) +static bool pack_snapshot_node(const snapshot_node_t *node, packed_buffer_t *pbuf) { bool result; /* Bilan à retourner */ size_t i; /* Boucle de parcours */ @@ -702,9 +700,6 @@ static void g_db_snapshot_class_init(GDbSnapshotClass *klass) static void g_db_snapshot_init(GDbSnapshot *snap) { - snap->tmpdir = NULL; - snap->hash = NULL; - snap->nodes = NULL; snap->current = NULL; @@ -746,12 +741,6 @@ static void g_db_snapshot_dispose(GDbSnapshot *snap) 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); @@ -765,8 +754,7 @@ static void g_db_snapshot_finalize(GDbSnapshot *snap) /****************************************************************************** * * -* Paramètres : tmpdir = répertoire de travail temporaire. * -* hash = empreinte du binaire à représenter. * +* Paramètres : archive = archive associée à l'instantané. * * * * Description : Prépare un gestionnaire d'instantanés de bases de données. * * * @@ -776,28 +764,15 @@ static void g_db_snapshot_finalize(GDbSnapshot *snap) * * ******************************************************************************/ -static GDbSnapshot *g_db_snapshot_new(const char *tmpdir, const char *hash) +static GDbSnapshot *g_db_snapshot_new(const GCdbArchive *archive) { GDbSnapshot *result; /* Adresse à retourner */ - int ret; /* Bilan d'une génération */ - bool status; /* Bilan de la création */ result = g_object_new(G_TYPE_DB_SNAPSHOT, NULL); - result->tmpdir = strdup(tmpdir); - result->hash = strdup(hash); + result->current_db = g_cdb_archive_get_tmp_filename(archive, "current_db.sql"); - ret = asprintf(&result->current_db, "%s" G_DIR_SEPARATOR_S "%s_current_db.sql", tmpdir, hash); - - status = (ret > 0); - - if (status) - { - ret = ensure_path_exists(result->current_db); - status = (ret != -1); - } - - if (!status) + if (result->current_db == NULL) { g_object_unref(G_OBJECT(result)); result = NULL; @@ -810,8 +785,7 @@ static GDbSnapshot *g_db_snapshot_new(const char *tmpdir, const char *hash) /****************************************************************************** * * -* Paramètres : tmpdir = répertoire de travail temporaire. * -* hash = empreinte du binaire à représenter. * +* Paramètres : archive = archive associée à l'instantané. * * collections = ensemble de modifications par catégories. * * * * Description : Prépare un gestionnaire d'instantanés de bases de données. * @@ -822,7 +796,7 @@ static GDbSnapshot *g_db_snapshot_new(const char *tmpdir, const char *hash) * * ******************************************************************************/ -GDbSnapshot *g_db_snapshot_new_empty(const char *tmpdir, const char *hash, GList *collections) +GDbSnapshot *g_db_snapshot_new_empty(const GCdbArchive *archive, GList *collections) { GDbSnapshot *result; /* Adresse à retourner */ sqlite3 *db; /* Base de données à manipuler */ @@ -831,12 +805,12 @@ GDbSnapshot *g_db_snapshot_new_empty(const char *tmpdir, const char *hash, GList GList *iter; /* Boucle de parcours */ GDbCollection *collec; /* Collection visée manipulée */ - result = g_db_snapshot_new(tmpdir, hash); + result = g_db_snapshot_new(archive); if (result == NULL) goto exit; result->nodes = create_snapshot_node(NULL, 0, NULL, NULL); - status = setup_snapshot_node_db_path(result->nodes, tmpdir, hash); + status = setup_snapshot_node_db_path(result->nodes, archive); if (!status) goto error; result->current = result->nodes; @@ -886,8 +860,7 @@ GDbSnapshot *g_db_snapshot_new_empty(const char *tmpdir, const char *hash, GList /****************************************************************************** * * -* Paramètres : tmpdir = répertoire de travail temporaire. * -* hash = empreinte du binaire à représenter. * +* Paramètres : archive = archive associée à l'instantané. * * xdoc = document XML à compléter. * * context = contexte pour les recherches. * * * @@ -899,7 +872,7 @@ GDbSnapshot *g_db_snapshot_new_empty(const char *tmpdir, const char *hash, GList * * ******************************************************************************/ -GDbSnapshot *g_db_snapshot_new_from_xml(const char *tmpdir, const char *hash, xmlDocPtr xdoc, xmlXPathContextPtr context) +GDbSnapshot *g_db_snapshot_new_from_xml(const GCdbArchive *archive, xmlDocPtr xdoc, xmlXPathContextPtr context) { GDbSnapshot *result; /* Adresse à retourner */ xmlXPathObjectPtr xobject; /* Cible d'une recherche */ @@ -916,7 +889,7 @@ GDbSnapshot *g_db_snapshot_new_from_xml(const char *tmpdir, const char *hash, xm snapshot_node_t *node; /* Instantané nouveau constitué*/ snapshot_id_t node_id; /* Identifiant de noeud courant*/ - result = g_db_snapshot_new(tmpdir, hash); + result = g_db_snapshot_new(archive); if (result == NULL) goto exit; /* Chargement de l'ensemble des instantanés */ @@ -1042,7 +1015,8 @@ GDbSnapshot *g_db_snapshot_new_from_xml(const char *tmpdir, const char *hash, xm /****************************************************************************** * * * Paramètres : snap = gestionnaire d'instantanés à constituer. * -* archive = archive en cours de lecture. * +* ar = archive en cours de lecture. * +* archive = archive associée à l'instantané. * * * * Description : Associe une base de données aux instantanés chargés. * * * @@ -1052,7 +1026,7 @@ GDbSnapshot *g_db_snapshot_new_from_xml(const char *tmpdir, const char *hash, xm * * ******************************************************************************/ -bool g_db_snapshot_fill(GDbSnapshot *snap, struct archive *archive) +bool g_db_snapshot_fill(GDbSnapshot *snap, struct archive *ar, const GCdbArchive *archive) { bool result; /* Bilan à retourner */ struct archive_entry *entry; /* Elément de l'archive */ @@ -1066,9 +1040,9 @@ bool g_db_snapshot_fill(GDbSnapshot *snap, struct archive *archive) result = false; - for (ret = archive_read_next_header(archive, &entry); + for (ret = archive_read_next_header(ar, &entry); ret == ARCHIVE_OK; - ret = archive_read_next_header(archive, &entry)) + ret = archive_read_next_header(ar, &entry)) { path = archive_entry_pathname(entry); @@ -1077,7 +1051,7 @@ bool g_db_snapshot_fill(GDbSnapshot *snap, struct archive *archive) if (strcmp(path, "current.db") == 0) { - if (!dump_archive_entry_into_file(archive, entry, snap->current_db)) + if (!dump_archive_entry_into_file(ar, entry, snap->current_db)) break; continue; @@ -1098,10 +1072,10 @@ bool g_db_snapshot_fill(GDbSnapshot *snap, struct archive *archive) if (node == NULL) break; - if (!setup_snapshot_node_db_path(node, snap->tmpdir, snap->hash)) + if (!setup_snapshot_node_db_path(node, archive)) break; - if (!dump_archive_entry_into_file(archive, entry, node->path)) + if (!dump_archive_entry_into_file(ar, entry, node->path)) break; } @@ -1263,7 +1237,7 @@ sqlite3 *g_db_snapshot_get_database(const GDbSnapshot *snap) * * ******************************************************************************/ -bool g_db_snapshot_pack_all(const GDbSnapshot *snap, packed_buffer *pbuf) +bool g_db_snapshot_pack_all(const GDbSnapshot *snap, packed_buffer_t *pbuf) { bool result; /* Bilan à retourner */ @@ -1287,7 +1261,7 @@ bool g_db_snapshot_pack_all(const GDbSnapshot *snap, packed_buffer *pbuf) * * ******************************************************************************/ -DBError g_db_snapshot_set_name(const GDbSnapshot *snap, packed_buffer *pbuf) +DBError g_db_snapshot_set_name(const GDbSnapshot *snap, packed_buffer_t *pbuf) { DBError result; /* Conclusion à retourner */ snapshot_id_t id; /* Identifiant d'instantané */ @@ -1352,7 +1326,7 @@ DBError g_db_snapshot_set_name(const GDbSnapshot *snap, packed_buffer *pbuf) * * ******************************************************************************/ -DBError g_db_snapshot_set_desc(const GDbSnapshot *snap, packed_buffer *pbuf) +DBError g_db_snapshot_set_desc(const GDbSnapshot *snap, packed_buffer_t *pbuf) { DBError result; /* Conclusion à retourner */ snapshot_id_t id; /* Identifiant d'instantané */ @@ -1418,7 +1392,7 @@ DBError g_db_snapshot_set_desc(const GDbSnapshot *snap, packed_buffer *pbuf) * * ******************************************************************************/ -DBError g_db_snapshot_restore(GDbSnapshot *snap, packed_buffer *pbuf, bool *reload) +DBError g_db_snapshot_restore(GDbSnapshot *snap, packed_buffer_t *pbuf, bool *reload) { DBError result; /* Conclusion à retourner */ snapshot_id_t id; /* Identifiant d'instantané */ @@ -1482,8 +1456,9 @@ DBError g_db_snapshot_restore(GDbSnapshot *snap, packed_buffer *pbuf, bool *relo /****************************************************************************** * * -* Paramètres : snap = gestionnaire d'instantanés à consulter. * -* db = base de données courante. * +* Paramètres : snap = gestionnaire d'instantanés à consulter. * +* db = base de données courante. * +* archive = archive associée à l'instantané. * * * * Description : Crée un nouvel instantanés dans l'arborescence. * * * @@ -1493,7 +1468,7 @@ DBError g_db_snapshot_restore(GDbSnapshot *snap, packed_buffer *pbuf, bool *relo * * ******************************************************************************/ -DBError g_db_snapshot_create(GDbSnapshot *snap, sqlite3 *db) +DBError g_db_snapshot_create(GDbSnapshot *snap, sqlite3 *db, const GCdbArchive *archive) { DBError result; /* Conclusion à retourner */ snapshot_node_t *new; /* Nouvel instantané */ @@ -1503,7 +1478,7 @@ DBError g_db_snapshot_create(GDbSnapshot *snap, sqlite3 *db) new = create_snapshot_node(NULL, 0, NULL, NULL); - status = setup_snapshot_node_db_path(new, snap->tmpdir, snap->hash); + status = setup_snapshot_node_db_path(new, archive); if (!status) { result = DBE_SYS_ERROR; @@ -1544,7 +1519,7 @@ DBError g_db_snapshot_create(GDbSnapshot *snap, sqlite3 *db) * * ******************************************************************************/ -DBError g_db_snapshot_remove(GDbSnapshot *snap, packed_buffer *pbuf, bool *changed) +DBError g_db_snapshot_remove(GDbSnapshot *snap, packed_buffer_t *pbuf, bool *changed) { DBError result; /* Conclusion à retourner */ snapshot_id_t id; /* Identifiant d'instantané */ diff --git a/src/analysis/db/snapshot.h b/src/analysis/db/snapshot.h index 8737f8c..59a2ba7 100644 --- a/src/analysis/db/snapshot.h +++ b/src/analysis/db/snapshot.h @@ -33,6 +33,7 @@ #include <libxml/xpath.h> +#include "cdb.h" #include "protocol.h" #include "misc/snapshot.h" @@ -57,13 +58,13 @@ typedef struct _GDbSnapshotClass GDbSnapshotClass; GType g_db_snapshot_get_type(void); /* Prépare un gestionnaire d'instantanés de bases de données. */ -GDbSnapshot *g_db_snapshot_new_empty(const char *, const char *, GList *); +GDbSnapshot *g_db_snapshot_new_empty(const GCdbArchive *, GList *); /* Charge un gestionnaire d'instantanés de bases de données. */ -GDbSnapshot *g_db_snapshot_new_from_xml(const char *, const char *, xmlDocPtr, xmlXPathContextPtr); +GDbSnapshot *g_db_snapshot_new_from_xml(const GCdbArchive *, xmlDocPtr, xmlXPathContextPtr); /* Associe une base de données aux instantanés chargés. */ -bool g_db_snapshot_fill(GDbSnapshot *, struct archive *); +bool g_db_snapshot_fill(GDbSnapshot *, struct archive *, const GCdbArchive *); /* Enregistre tous les éléments associés aux instantanés. */ DBError g_db_snapshot_save(const GDbSnapshot *, xmlDocPtr, xmlXPathContextPtr, struct archive *); @@ -75,22 +76,22 @@ bool g_db_snapshot_get_current_id(const GDbSnapshot *, snapshot_id_t *); sqlite3 *g_db_snapshot_get_database(const GDbSnapshot *); /* Collecte les descriptions de l'ensemble des instantanés. */ -bool g_db_snapshot_pack_all(const GDbSnapshot *, packed_buffer *); +bool g_db_snapshot_pack_all(const GDbSnapshot *, packed_buffer_t *); /* Actualise la désignation d'un instantané donné. */ -DBError g_db_snapshot_set_name(const GDbSnapshot *, packed_buffer *); +DBError g_db_snapshot_set_name(const GDbSnapshot *, packed_buffer_t *); /* Actualise la description d'un instantané donné. */ -DBError g_db_snapshot_set_desc(const GDbSnapshot *, packed_buffer *); +DBError g_db_snapshot_set_desc(const GDbSnapshot *, packed_buffer_t *); /* Restaure un instantané de l'arborescence. */ -DBError g_db_snapshot_restore(GDbSnapshot *, packed_buffer *, bool *); +DBError g_db_snapshot_restore(GDbSnapshot *, packed_buffer_t *, bool *); /* Crée un nouvel instantanés dans l'arborescence. */ -DBError g_db_snapshot_create(GDbSnapshot *, sqlite3 *); +DBError g_db_snapshot_create(GDbSnapshot *, sqlite3 *, const GCdbArchive *); /* Supprime un instantané dans l'arborescence. */ -DBError g_db_snapshot_remove(GDbSnapshot *, packed_buffer *, bool *); +DBError g_db_snapshot_remove(GDbSnapshot *, packed_buffer_t *, bool *); |