From 2ea2e6080eec1b06bbd4607203d34f38b75c80f3 Mon Sep 17 00:00:00 2001
From: Cyrille Bagard <nocbos@gmail.com>
Date: Sun, 1 Aug 2021 18:46:12 +0200
Subject: Extend the protocol to store the analyzed binary content.

---
 plugins/pychrysalide/analysis/db/analyst.c |  49 +++++++
 src/analysis/db/analyst.c                  | 115 +++++++++++++--
 src/analysis/db/analyst.h                  |   4 +
 src/analysis/db/auth.c                     | 152 +++++++++++++++++++-
 src/analysis/db/auth.h                     |   5 +-
 src/analysis/db/cdb.c                      | 218 ++++++++++++++++++++++++++++-
 src/analysis/db/protocol.h                 |  64 +++++++--
 7 files changed, 583 insertions(+), 24 deletions(-)

diff --git a/plugins/pychrysalide/analysis/db/analyst.c b/plugins/pychrysalide/analysis/db/analyst.c
index 83e3878..bb9af30 100644
--- a/plugins/pychrysalide/analysis/db/analyst.c
+++ b/plugins/pychrysalide/analysis/db/analyst.c
@@ -36,6 +36,7 @@
 
 #include "client.h"
 #include "collection.h"
+#include "../content.h"
 #include "../../access.h"
 #include "../../helpers.h"
 #include "../../struct.h"
@@ -45,6 +46,9 @@
 /* Crée un nouvel objet Python de type 'AnalystClient'. */
 static PyObject *py_analyst_client_new(PyTypeObject *, PyObject *, PyObject *);
 
+/* Envoie un contenu binaire pour conservation côté serveur. */
+static PyObject *py_analyst_client_send_content(PyObject *, PyObject *);
+
 /* Effectue une demande de sauvegarde de l'état courant. */
 static PyObject *py_analyst_client_save(PyObject *, PyObject *);
 
@@ -179,6 +183,50 @@ static PyObject *py_analyst_client_new(PyTypeObject *type, PyObject *args, PyObj
 /******************************************************************************
 *                                                                             *
 *  Paramètres  : self = client à manipuler.                                   *
+*                args = arguments fournis à l'appel.                          *
+*                                                                             *
+*  Description : Envoie un contenu binaire pour conservation côté serveur.    *
+*                                                                             *
+*  Retour      : True si la commande a bien été envoyée, False sinon.         *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static PyObject *py_analyst_client_send_content(PyObject *self, PyObject *args)
+{
+    PyObject *result;                       /* Bilan à retourner           */
+    GBinContent *content;                   /* Contenu binaire à envoyer   */
+    int ret;                                /* Bilan de lecture des args.  */
+    GAnalystClient *client;                 /* Version native du serveur   */
+    bool status;                            /* Bilan de l'opération        */
+
+#define ANALYST_CLIENT_SEND_CONTENT_METHOD PYTHON_METHOD_DEF                \
+(                                                                           \
+    send_content, "$self, content, /",                                      \
+    METH_VARARGS, py_analyst_client,                                        \
+    "Ask the server for saving the current state of the analyzed binary"    \
+    " and returns the status of the request transmission."                  \
+)
+
+    ret = PyArg_ParseTuple(args, "O&", convert_to_binary_content, &content);
+    if (!ret) return NULL;
+
+    client = G_ANALYST_CLIENT(pygobject_get(self));
+
+    status = g_analyst_client_send_content(client, content);
+
+    result = status ? Py_True : Py_False;
+    Py_INCREF(result);
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : self = client à manipuler.                                   *
 *                args = arguments d'appel non utilisés ici.                   *
 *                                                                             *
 *  Description : Effectue une demande de sauvegarde de l'état courant.        *
@@ -773,6 +821,7 @@ static int py_analyst_client_set_current_snapshot(PyObject *self, PyObject *valu
 PyTypeObject *get_python_analyst_client_type(void)
 {
     static PyMethodDef py_analyst_client_methods[] = {
+        ANALYST_CLIENT_SEND_CONTENT_METHOD,
         ANALYST_CLIENT_SAVE_METHOD,
         ANALYST_CLIENT_SET_LAST_ACTIVE_METHOD,
         ANALYST_CLIENT_SET_SNAPSHOT_NAME_METHOD,
diff --git a/src/analysis/db/analyst.c b/src/analysis/db/analyst.c
index dff073a..49585c2 100644
--- a/src/analysis/db/analyst.c
+++ b/src/analysis/db/analyst.c
@@ -26,9 +26,11 @@
 
 #include <assert.h>
 #include <poll.h>
+#include <string.h>
 
 
 #include "client-int.h"
+#include "../storage/storage.h"
 #include "../../core/logs.h"
 
 
@@ -38,7 +40,7 @@ struct _GAnalystClient
 {
     GHubClient parent;                      /* A laisser en premier        */
 
-    rle_string hash;                        /* Empreinte du binaire lié    */
+    char *hash;                             /* Empreinte du binaire lié    */
     GList *collections;                     /* Collections d'un binaire    */
 
     bool can_get_updates;                   /* Réception de maj possibles ?*/
@@ -157,7 +159,7 @@ static void g_analyst_client_class_init(GAnalystClientClass *klass)
 
 static void g_analyst_client_init(GAnalystClient *client)
 {
-    setup_empty_rle_string(&client->hash);
+    client->hash = NULL;
     client->collections = NULL;
 
     client->can_get_updates = false;
@@ -214,7 +216,8 @@ static void g_analyst_client_finalize(GAnalystClient *client)
 {
     size_t i;                               /* Boucle de parcours          */
 
-    unset_rle_string(&client->hash);
+    if (client->hash != NULL)
+        free(client->hash);
 
     if (client->snapshots != NULL)
     {
@@ -249,7 +252,7 @@ GAnalystClient *g_analyst_client_new(const char *hash, GList *collections)
 
     result = g_object_new(G_TYPE_ANALYST_CLIENT, NULL);
 
-    init_static_rle_string(&result->hash, hash);
+    result->hash = strdup(hash);
     result->collections = collections;
 
     return result;
@@ -273,8 +276,13 @@ GAnalystClient *g_analyst_client_new(const char *hash, GList *collections)
 static bool g_analyst_client_complete_hello(GAnalystClient *client, packed_buffer_t *pbuf)
 {
     bool result;                            /* Bilan à retourner           */
+    rle_string str;                         /* Chaîne à communiquer        */
 
-    result = pack_rle_string(&client->hash, pbuf);
+    init_static_rle_string(&str, client->hash);
+
+    result = pack_rle_string(&str, pbuf);
+
+    exit_rle_string(&str);
 
     return result;
 
@@ -406,11 +414,10 @@ static void *g_analyst_client_update(GAnalystClient *client)
                     error = tmp32;
 
                     if (error == DBE_NONE)
-                        log_variadic_message(LMT_INFO, _("Archive saved for binary '%s'"),
-                                             get_rle_string(&client->hash));
+                        log_variadic_message(LMT_INFO, _("Archive saved for binary '%s'"), client->hash);
                     else
                         log_variadic_message(LMT_ERROR, _("Failed to save the archive for binary '%s'"),
-                                             get_rle_string(&client->hash));
+                                             client->hash);
 
                     break;
 
@@ -635,6 +642,98 @@ static bool g_analyst_client_update_current_snapshot(GAnalystClient *client, pac
 
 /******************************************************************************
 *                                                                             *
+*  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->hash) != 0)
+    {
+        log_variadic_message(LMT_ERROR, _("Provided ontent does not match client content (hash: '%s')"),
+                             client->hash);
+        goto exit;
+    }
+
+    /* Conversion en format de stockage */
+
+    init_packed_buffer(&cnt_pbuf);
+
+    storage = g_object_storage_new(client->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.        *
diff --git a/src/analysis/db/analyst.h b/src/analysis/db/analyst.h
index d9e90c6..7b11f53 100644
--- a/src/analysis/db/analyst.h
+++ b/src/analysis/db/analyst.h
@@ -33,6 +33,7 @@
 #include "client.h"
 #include "collection.h"
 #include "misc/snapshot.h"
+#include "../content.h"
 
 
 
@@ -57,6 +58,9 @@ GType g_analyst_client_get_type(void);
 /* Prépare un client pour une connexion à une BD. */
 GAnalystClient *g_analyst_client_new(const char *, GList *);
 
+/* 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 *);
 
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/cdb.c b/src/analysis/db/cdb.c
index 4da55fd..e4179d3 100644
--- a/src/analysis/db/cdb.c
+++ b/src/analysis/db/cdb.c
@@ -26,6 +26,7 @@
 
 #include <assert.h>
 #include <errno.h>
+#include <fcntl.h>
 #include <malloc.h>
 #include <poll.h>
 #include <pthread.h>
@@ -45,9 +46,12 @@
 #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"
@@ -79,6 +83,7 @@ struct _GCdbArchive
 
     char *filename;                         /* Chemin d'accès à l'archive  */
     char *tmpdir;                           /* Répertoire de travail       */
+    char *cnt_file;                         /* Fichier de contenu binaire  */
     char *xml_desc;                         /* Fichier de description      */
 
     GList *collections;                     /* Ensemble de modifications   */
@@ -163,6 +168,17 @@ static bool g_cdb_archive_send_snapshot_change(GCdbArchive *, packed_buffer_t *)
 
 
 
+/* ------------------------- PRISES EN COMPTE DES COMMANDES ------------------------- */
+
+
+/* Prépare la réponse à envoyer à un client connecté. */
+static bool setup_server_answer(DBCommand, DBError, packed_buffer_t *);
+
+/* Enregistre le contenu binaire lié à une analyse. */
+static bool g_cdb_archive_set_content(GCdbArchive *, packed_buffer_t *, packed_buffer_t *);
+
+
+
 /* ---------------------------------------------------------------------------------- */
 /*                           COEUR DE LA GESTION D'ARCHIVES                           */
 /* ---------------------------------------------------------------------------------- */
@@ -222,6 +238,8 @@ static void g_cdb_archive_init(GCdbArchive *archive)
 
     archive->filename = NULL;
     archive->tmpdir = NULL;
+    archive->cnt_file = NULL;
+    archive->xml_desc = NULL;
 
     archive->collections = create_collections_list();
 
@@ -285,15 +303,18 @@ 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);
 
-    if (archive->filename != NULL)
-        free(archive->filename);
-
     if (archive->tmpdir != NULL)
         free(archive->tmpdir);
 
+    if (archive->filename != NULL)
+        free(archive->filename);
+
     exit_rle_string(&archive->hash);
 
     G_OBJECT_CLASS(g_cdb_archive_parent_class)->finalize(G_OBJECT(archive));
@@ -932,6 +953,19 @@ static void *g_cdb_archive_process(GCdbArchive *archive)
 
                 switch (command)
                 {
+                    case DBC_SET_CONTENT:
+
+                        status = g_cdb_archive_set_content(archive, &in_pbuf, &out_pbuf);
+                        if (!status) goto gcap_bad_reply;
+
+                        status = ssl_send_packed_buffer(&out_pbuf, archive->clients[i].tls_fd);
+                        if (!status) goto gcap_bad_reply;
+
+                        exit_packed_buffer(&out_pbuf);
+                        break;
+
+
+
                     case DBC_SAVE:
 
                         error = g_cdb_archive_write(archive);
@@ -1451,3 +1485,181 @@ static bool g_cdb_archive_send_snapshot_change(GCdbArchive *archive, packed_buff
     return result;
 
 }
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/*                           PRISES EN COMPTE DES COMMANDES                           */
+/* ---------------------------------------------------------------------------------- */
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : cmd      = commande à l'origine d'un traitement.             *
+*                error    = bilan de traitement à communiquer.                *
+*                out_pbuf = paquet à consituer pour un retour au client. [OUT]*
+*                                                                             *
+*  Description : Prépare la réponse à envoyer à un client connecté.           *
+*                                                                             *
+*  Retour      : Indication pour le maintien de la communication.             *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static bool setup_server_answer(DBCommand cmd, DBError error, packed_buffer_t *out_pbuf)
+{
+    bool result;                            /* Bilan à retourner           */
+
+    init_packed_buffer(out_pbuf);
+
+    result = extend_packed_buffer(out_pbuf, (uint32_t []) { cmd }, sizeof(uint32_t), true);
+
+    if (result)
+        result = extend_packed_buffer(out_pbuf, (uint32_t []) { error }, sizeof(uint32_t), true);
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  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   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+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 ret;                                /* Retour d'un appel           */
+    int fd;                                 /* Flux ouvert en écriture     */
+    bool status;                            /* Bilan d'une écriture        */
+
+    result = true;
+    error = DBE_NONE;
+
+    /* 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);
+
+    result = extend_packed_buffer(&test_pbuf, data, data_length, false);
+    if (!result) goto check_failure;
+
+    rewind_packed_buffer(&test_pbuf);
+
+    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;
+    }
+
+    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;
+
+    g_object_unref(G_OBJECT(content));
+
+ storage_check_failure:
+
+    g_object_unref(G_OBJECT(storage));
+
+ check_failure:
+
+    exit_packed_buffer(&test_pbuf);
+
+    if (!result) goto free_and_exit;
+
+    /* Enregistrement sur disque */
+
+    if (error == DBE_NONE)
+    {
+        if (archive->cnt_file != NULL)
+            free(archive->cnt_file);
+
+        ret = asprintf(&archive->cnt_file, "%s" G_DIR_SEPARATOR_S "%s_storedcontent.bin",
+                       archive->tmpdir, get_rle_string(&archive->hash));
+        if (ret == -1)
+        {
+            error = DBE_SYS_ERROR;
+            goto save_error;
+        }
+
+        fd = open(archive->cnt_file, O_WRONLY | O_CREAT, 0600);
+        if (fd == -1)
+        {
+            error = DBE_SYS_ERROR;
+            goto save_error;
+        }
+
+        status = safe_write(fd, data, data_length);
+
+        if (!status)
+        {
+            unlink(archive->cnt_file);
+            free(archive->cnt_file);
+            archive->cnt_file = NULL;
+
+            error = DBE_SYS_ERROR;
+
+        }
+
+        close(fd);
+
+ save_error:
+
+        ;
+
+    }
+
+    /* Formulation de la réponse */
+
+    result = setup_server_answer(DBC_SET_CONTENT, error, out_pbuf);
+
+ free_and_exit:
+
+    free(data);
+
+ exit:
+
+    return result;
+
+}
diff --git a/src/analysis/db/protocol.h b/src/analysis/db/protocol.h
index 4bef28e..17263c8 100644
--- a/src/analysis/db/protocol.h
+++ b/src/analysis/db/protocol.h
@@ -47,15 +47,6 @@
 
 
 
-/* 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;
 
 
 
@@ -71,17 +62,36 @@ typedef enum _ClientRole
 
 } 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 :
+
+/* Eléments de base nécessaires */
+typedef enum _RequiredBasics
+{
+    RBS_NONE    = 0x0,                      /* (Plus) rien n'est requis    */
+    RBS_CONTENT = 0x1,                      /* Contenu binaire à analyser  */
+    RBS_LOADED  = 0x2,                      /* Contenu binaire analysé     */
+
+} RequiredBasics;
+
 
 
 
 
 
+/**
+ * Une fois la connexion établie, les paquets ont tous la forme suivante :
  *
  *    [ type de collection visée ; cf. DBFeatures ]
  *    [ action à mener ; cf. DBAction             ]
@@ -166,6 +176,37 @@ typedef enum _DBCommand
     /* ------------------------ Commandes pour analyste ------------------------ */
 
     /**
+     * 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'.
      *
      * Le client connecté envoie un paquet de la forme suivante :
@@ -377,6 +418,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    */
-- 
cgit v0.11.2-87-g4458