From 1865cce4d51b9c7a6fb718f4e2c034a57365ad1b Mon Sep 17 00:00:00 2001 From: Cyrille Bagard Date: Mon, 31 May 2021 01:40:39 +0200 Subject: Create a command to list remote binaries. --- plugins/pychrysalide/analysis/db/admin.c | 116 +++++++++++++++-- src/analysis/db/admin.c | 209 +++++++++++++++++++++++++++++++ src/analysis/db/admin.h | 6 + src/analysis/db/controller.c | 122 +++++++++++------- src/analysis/db/protocol.h | 20 ++- tests/analysis/db/conn.py | 20 ++- 6 files changed, 425 insertions(+), 68 deletions(-) diff --git a/plugins/pychrysalide/analysis/db/admin.c b/plugins/pychrysalide/analysis/db/admin.c index 52027ac..a4694e6 100644 --- a/plugins/pychrysalide/analysis/db/admin.c +++ b/plugins/pychrysalide/analysis/db/admin.c @@ -42,6 +42,12 @@ /* Crée un nouvel objet Python de type 'AdminClient'. */ static PyObject *py_admin_client_new(PyTypeObject *, PyObject *, PyObject *); +/* Effectue une demande de liste de binaires existants. */ +static PyObject *py_admin_client_request_existing_binaries(PyObject *, PyObject *); + +/* Fournit la liste des instantanés existants. */ +static PyObject *py_admin_client_get_existing_binaries(PyObject *, void *); + /****************************************************************************** @@ -63,9 +69,9 @@ static PyObject *py_admin_client_new(PyTypeObject *type, PyObject *args, PyObjec PyObject *result; /* Instance à retourner */ GAdminClient *client; /* Serveur mis en place */ -#define ADMIN_CLIENT_DOC \ - "AdminClient provides and receives binary updates to and from a connected" \ - " to a server.\n" \ +#define ADMIN_CLIENT_DOC \ + "AdminClient provides control of the registered binary contents available from a" \ + " server.\n" \ "\n" \ "Such clients must be authenticated and communications are encrypted using TLS.\n" \ "\n" \ @@ -74,16 +80,9 @@ static PyObject *py_admin_client_new(PyTypeObject *type, PyObject *args, PyObjec " AdminClient()" \ "\n" \ "AdminClient instances emit the following signals:\n" \ - "* 'snapshots-updated'\n" \ - " This signal is emitted when the snapshot list has evolved.\n" \ - "\n" \ - " Handlers are expected to have only one argument: the client managing the" \ - " updated snapshots.\n" \ - "* 'snapshot-changed'\n" \ - " This signal is emitted when the identifier of the current snapshot changed.\n" \ - "\n" \ - " Handlers are expected to have only one argument: the client managing the" \ - " snapshots." + "* 'existing-binaries-updated'\n" \ + " This signal is emitted when the list of existing binaries on server side" \ + " has been updated following a user request.\n" \ client = g_admin_client_new(); @@ -101,6 +100,95 @@ static PyObject *py_admin_client_new(PyTypeObject *type, PyObject *args, PyObjec /****************************************************************************** * * +* Paramètres : self = client à manipuler. * +* args = arguments d'appel non utilisés ici. * +* * +* Description : Effectue une demande de liste de binaires existants. * +* * +* Retour : True si la commande a bien été envoyée, False sinon. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_admin_client_request_existing_binaries(PyObject *self, PyObject *args) +{ + PyObject *result; /* Bilan à retourner */ + GAdminClient *client; /* Version native du serveur */ + bool status; /* Bilan de l'opération */ + +#define ADMIN_CLIENT_REQUEST_EXISTING_BINARIES_METHOD PYTHON_METHOD_DEF \ +( \ + request_existing_binaries, "$self, /", \ + METH_NOARGS, py_admin_client, \ + "Ask the server for a list of all existing analyzed binaries" \ + " and returns the status of the request transmission." \ + "\n" \ + "A *existing-binaries-updated* signal is emitted when the" \ + " pychrysalide.analysis.db.AdminClient.existing_binaries attribute" \ + " gets ready for reading." \ +) + + client = G_ADMIN_CLIENT(pygobject_get(self)); + + status = g_admin_client_request_existing_binaries(client); + + result = status ? Py_True : Py_False; + Py_INCREF(result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet Python concerné par l'appel. * +* closure = non utilisé ici. * +* * +* Description : Fournit la liste des instantanés existants. * +* * +* Retour : Liste de binaires en place, vide si aucun. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_admin_client_get_existing_binaries(PyObject *self, void *closure) +{ + PyObject *result; /* Valeur à retourner */ + GAdminClient *client; /* Version native du serveur */ + size_t count; /* Taille de cette liste */ + char **binaries; /* Liste des binaires présents */ + size_t i; /* Boucle de parcours */ + +#define ADMIN_CLIENT_EXISTING_BINARIES_ATTRIB PYTHON_GET_DEF_FULL \ +( \ + existing_binaries, py_admin_client, \ + "Provide the list of all exisiting binaries on the server side.\n" \ + "\n" \ + "The returned value is a tuple of strings or an empty tuple." \ +) + + client = G_ADMIN_CLIENT(pygobject_get(self)); + + binaries = g_admin_client_get_existing_binaries(client, &count); + + result = PyTuple_New(count); + + for (i = 0; i < count; i++) + PyTuple_SetItem(result, i, PyUnicode_FromString(binaries[i])); + + if (binaries != NULL) + free(binaries); + + return result; + +} + + +/****************************************************************************** +* * * Paramètres : - * * * * Description : Fournit un accès à une définition de type à diffuser. * @@ -114,10 +202,12 @@ static PyObject *py_admin_client_new(PyTypeObject *type, PyObject *args, PyObjec PyTypeObject *get_python_admin_client_type(void) { static PyMethodDef py_admin_client_methods[] = { + ADMIN_CLIENT_REQUEST_EXISTING_BINARIES_METHOD, { NULL } }; static PyGetSetDef py_admin_client_getseters[] = { + ADMIN_CLIENT_EXISTING_BINARIES_ATTRIB, { NULL } }; diff --git a/src/analysis/db/admin.c b/src/analysis/db/admin.c index 355f8c0..771a912 100644 --- a/src/analysis/db/admin.c +++ b/src/analysis/db/admin.c @@ -25,10 +25,13 @@ #include +#include #include +#include #include "client-int.h" +#include "../../common/leb128.h" #include "../../core/logs.h" @@ -38,6 +41,10 @@ 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) */ @@ -45,6 +52,10 @@ struct _GAdminClientClass { GHubClientClass parent; /* A laisser en premier */ + /* Signaux */ + + void (* existing_binaries_updated) (GAdminClient *); + }; @@ -63,6 +74,9 @@ 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 *); + /* Indique le type défini pour une description de client à l'écoute. */ @@ -96,6 +110,14 @@ static void g_admin_client_class_init(GAdminClientClass *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); + } @@ -113,6 +135,9 @@ static void g_admin_client_class_init(GAdminClientClass *klass) static void g_admin_client_init(GAdminClient *client) { + client->binaries = NULL; + client->binaries_count = 0; + g_mutex_init(&client->binaries_lock); } @@ -152,6 +177,19 @@ static void g_admin_client_dispose(GAdminClient *client) 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)); } @@ -256,6 +294,11 @@ static void *g_admin_client_update(GAdminClient *client) 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); @@ -290,3 +333,169 @@ static void *g_admin_client_update(GAdminClient *client) 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 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 *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 index f5a6b5d..31366ae 100644 --- a/src/analysis/db/admin.h +++ b/src/analysis/db/admin.h @@ -57,6 +57,12 @@ 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/controller.c b/src/analysis/db/controller.c index 46b628d..a27c20a 100644 --- a/src/analysis/db/controller.c +++ b/src/analysis/db/controller.c @@ -26,6 +26,7 @@ #include #include +#include #include #include #include @@ -36,6 +37,8 @@ #include "backend-int.h" +#include "misc/rlestr.h" +#include "../../common/leb128.h" #include "../../common/packed.h" #include "../../core/logs.h" @@ -80,6 +83,9 @@ 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. */ @@ -238,12 +244,10 @@ 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 in_pbuf; /* Tampon de réception */ - uint32_t tmp32; /* Valeur sur 32 bits */ + packed_buffer_t in_pbuf; /* Tampon de réception */ 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 */ char *msg; /* Erreur à faire remonter */ base = G_SERVER_BACKEND(controller); @@ -305,27 +309,23 @@ static void *g_cdb_controller_process(GCdbController *controller) switch (command) { - case DBC_LIST_ARCHIVES: - - /* - error = g_cdb_controller_write(archive); + case DBC_LIST_BINARIES: - init_packed_buffer(&out_pbuf); + init_packed_buffer(&out_pbuf); - status = extend_packed_buffer(&out_pbuf, (uint32_t []) { DBC_SAVE }, - sizeof(uint32_t), true); - if (!status) goto reply_error; + status = extend_packed_buffer(&out_pbuf, (uint32_t []) { DBC_EXISTING_BINARIES }, + sizeof(uint32_t), true); + if (!status) goto reply_error; - status = extend_packed_buffer(&out_pbuf, (uint32_t []) { error }, 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; - */ + status = ssl_send_packed_buffer(&out_pbuf, controller->tls_fd); + if (!status) goto reply_error; - exit_packed_buffer(&out_pbuf); + exit_packed_buffer(&out_pbuf); - break; + break; default: asprintf(&msg, _("Bad protocol command: 0x%08x"), command); @@ -397,16 +397,12 @@ static void g_cdb_controller_add_client(GCdbController *controller, SSL *fd, con } - - - -#if 0 /****************************************************************************** * * -* Paramètres : controller = archive à connecter avec un utilisateur. * -* pbuf = paquet à consituer pour un envoi unique. [OUT] * +* Paramètres : controller = administration d'archives d'analyse. * +* pbuf = paquet à consituer pour un envoi unique. [OUT] * * * -* Description : Envoie à tous les clients la nouvelle liste d'instantanés. * +* Description : Envoie au client la liste des binaires présents. * * * * Retour : Bilan de constitution de la réponse. * * * @@ -414,36 +410,72 @@ static void g_cdb_controller_add_client(GCdbController *controller, SSL *fd, con * * ******************************************************************************/ -static bool g_cdb_controller_send_snapshot_update(GCdbController *controller, packed_buffer *pbuf) +static bool g_cdb_controller_send_existing_binaries(GCdbController *controller, packed_buffer_t *pbuf) { bool result; /* Bilan à retourner */ - bool do_send; /* Réalisation de l'émission */ - packed_buffer out_pbuf; /* Tampon d'émission */ + 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"); - do_send = (pbuf == NULL); + if (errno == ENOENT) + { + count = 0; + result = pack_uleb128(&count, pbuf); + } - if (pbuf == NULL) - pbuf = &out_pbuf; + goto bad_dir; - init_packed_buffer(pbuf); + } - result = extend_packed_buffer(pbuf, (uint32_t []) { DBC_SNAPSHOTS_UPDATED }, - sizeof(uint32_t), true); - if (!result) goto bad_reply; + count = 0; + init_packed_buffer(&items); - result = g_db_snapshot_pack_all(archive->snapshot, pbuf); - if (!result) goto bad_reply; + for (item = readdir(directory); item != NULL; item = readdir(directory)) + { + if (item->d_type != DT_DIR) + continue; - result = extend_packed_buffer(pbuf, SNAPSHOT_END_MARK, SNAP_ID_HEX_SZ, false); - if (!result) goto bad_reply; + if (strcmp(item->d_name, ".") == 0 || strcmp(item->d_name, "..") == 0) + continue; - bad_reply: + init_static_rle_string(&name, item->d_name); - if (do_send || !result) - exit_packed_buffer(pbuf); + 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; } - -#endif diff --git a/src/analysis/db/protocol.h b/src/analysis/db/protocol.h index 41e3ae7..4bef28e 100644 --- a/src/analysis/db/protocol.h +++ b/src/analysis/db/protocol.h @@ -126,8 +126,6 @@ typedef enum _DBAction typedef enum _DBCommand { /** - * Gestion des commandes 'DBC_HELO' et 'DBC_WELCOME'. - * * Le client envoie un tout premier paquet de la forme suivante : * * [ Ordre de sauvegarde : DBC_HELO ] @@ -148,11 +146,21 @@ typedef enum _DBCommand /* ------------------------ 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_ARCHIVES, /* Fourniture des identifiants */ - - + DBC_LIST_BINARIES, /* Fourniture des identifiants */ + DBC_EXISTING_BINARIES, /* Eléments présents */ /* ------------------------ Commandes pour analyste ------------------------ */ diff --git a/tests/analysis/db/conn.py b/tests/analysis/db/conn.py index 39c660a..f388f60 100644 --- a/tests/analysis/db/conn.py +++ b/tests/analysis/db/conn.py @@ -6,6 +6,7 @@ from pychrysalide.analysis.db import HubServer import os import shutil import tempfile +import threading class TestDbConnection(ChrysalideTestCase): @@ -113,13 +114,24 @@ class TestDbConnection(ChrysalideTestCase): #print(ret) - admin = AdminClient() - #print(admin) + admin = AdminClient() + ret = admin.start('localhost', '9999') + self.assertTrue(ret) - #print('FINAL::', ret) + def _on_existing_binaries_updated(adm, evt): + evt.set() - #print(server) + event = threading.Event() + + admin.connect('existing-binaries-updated', _on_existing_binaries_updated, event) + + ret = admin.request_existing_binaries() + self.assertTrue(ret) + + event.wait() + + self.assertEqual(len(admin.existing_binaries), 0) -- cgit v0.11.2-87-g4458