From 3c493d4cd2c9e91a2cee08c80e3629ea75788605 Mon Sep 17 00:00:00 2001 From: Cyrille Bagard Date: Sat, 29 May 2021 12:13:16 +0200 Subject: Define roles for allowed connections to servers. --- plugins/pychrysalide/analysis/binary.c | 5 +- plugins/pychrysalide/analysis/db/Makefile.am | 2 + plugins/pychrysalide/analysis/db/admin.c | 227 +++++ plugins/pychrysalide/analysis/db/admin.h | 45 + plugins/pychrysalide/analysis/db/analyst.c | 895 ++++++++++++++++++++ plugins/pychrysalide/analysis/db/analyst.h | 45 + plugins/pychrysalide/analysis/db/client.c | 713 +--------------- plugins/pychrysalide/analysis/db/module.c | 4 + plugins/pychrysalide/analysis/db/server.c | 3 +- src/analysis/binary.c | 36 +- src/analysis/binary.h | 4 +- src/analysis/db/Makefile.am | 6 + src/analysis/db/admin.c | 292 +++++++ src/analysis/db/admin.h | 62 ++ src/analysis/db/analyst.c | 1136 +++++++++++++++++++++++++ src/analysis/db/analyst.h | 95 +++ src/analysis/db/backend-int.h | 68 ++ src/analysis/db/backend.c | 267 ++++++ src/analysis/db/backend.h | 56 ++ src/analysis/db/cdb.c | 349 ++++---- src/analysis/db/cdb.h | 9 - src/analysis/db/client-int.h | 71 ++ src/analysis/db/client.c | 1147 ++------------------------ src/analysis/db/client.h | 39 - src/analysis/db/controller.c | 449 ++++++++++ src/analysis/db/controller.h | 59 ++ src/analysis/db/protocol.h | 42 +- src/analysis/db/server.c | 424 ++++++++-- src/gui/dialogs/snapshots.c | 48 +- src/gui/panels/history.c | 8 +- tests/analysis/db/conn.py | 125 +++ 31 files changed, 4595 insertions(+), 2136 deletions(-) create mode 100644 plugins/pychrysalide/analysis/db/admin.c create mode 100644 plugins/pychrysalide/analysis/db/admin.h create mode 100644 plugins/pychrysalide/analysis/db/analyst.c create mode 100644 plugins/pychrysalide/analysis/db/analyst.h create mode 100644 src/analysis/db/admin.c create mode 100644 src/analysis/db/admin.h create mode 100644 src/analysis/db/analyst.c create mode 100644 src/analysis/db/analyst.h create mode 100644 src/analysis/db/backend-int.h create mode 100644 src/analysis/db/backend.c create mode 100644 src/analysis/db/backend.h create mode 100644 src/analysis/db/client-int.h create mode 100644 src/analysis/db/controller.c create mode 100644 src/analysis/db/controller.h create mode 100644 tests/analysis/db/conn.py diff --git a/plugins/pychrysalide/analysis/binary.c b/plugins/pychrysalide/analysis/binary.c index 2f8af5f..68f2d88 100644 --- a/plugins/pychrysalide/analysis/binary.c +++ b/plugins/pychrysalide/analysis/binary.c @@ -127,7 +127,7 @@ static PyObject *py_loaded_binary_get_client(PyObject *self, PyObject *args) int internal; /* Nature du client visé */ int ret; /* Bilan de lecture des args. */ GLoadedBinary *binary; /* Binaire en cours d'analyse */ - GHubClient *client; /* Eventuel client en place */ + GAnalystClient *client; /* Eventuel client en place */ #define LOADED_BINARY_GET_CLIENT_METHOD PYTHON_METHOD_DEF \ ( \ @@ -135,6 +135,9 @@ static PyObject *py_loaded_binary_get_client(PyObject *self, PyObject *args) METH_VARARGS, py_loaded_binary, \ "Provide the client connected to an internal or remote server" \ " if defined, or return None otherwise.\n" \ + "\n" \ + "The returned object is a pychrysalide.analysis.db.AnalystClient" \ + " instance or *None*." \ ) internal = 1; diff --git a/plugins/pychrysalide/analysis/db/Makefile.am b/plugins/pychrysalide/analysis/db/Makefile.am index fdf491e..721f5b6 100644 --- a/plugins/pychrysalide/analysis/db/Makefile.am +++ b/plugins/pychrysalide/analysis/db/Makefile.am @@ -2,6 +2,8 @@ noinst_LTLIBRARIES = libpychrysaanalysisdb.la libpychrysaanalysisdb_la_SOURCES = \ + admin.h admin.c \ + analyst.h analyst.c \ certs.h certs.c \ client.h client.c \ collection.h collection.c \ diff --git a/plugins/pychrysalide/analysis/db/admin.c b/plugins/pychrysalide/analysis/db/admin.c new file mode 100644 index 0000000..52027ac --- /dev/null +++ b/plugins/pychrysalide/analysis/db/admin.c @@ -0,0 +1,227 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * admin.c - équivalent Python du fichier "analysis/db/admin.c" + * + * Copyright (C) 2019 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "admin.h" + + +#include +#include + + +#include +#include + + +#include "client.h" +#include "../../access.h" +#include "../../helpers.h" + + + +/* Crée un nouvel objet Python de type 'AdminClient'. */ +static PyObject *py_admin_client_new(PyTypeObject *, PyObject *, PyObject *); + + + +/****************************************************************************** +* * +* Paramètres : type = type de l'objet à instancier. * +* args = arguments fournis à l'appel. * +* kwds = arguments de type key=val fournis. * +* * +* Description : Crée un nouvel objet Python de type 'AdminClient'. * +* * +* Retour : Instance Python mise en place. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_admin_client_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + 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" \ + "\n" \ + "Such clients must be authenticated and communications are encrypted using TLS.\n" \ + "\n" \ + "Instances can be created using the following constructor:\n" \ + "\n" \ + " 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." + + client = g_admin_client_new(); + + if (client != NULL) + { + result = pygobject_new(G_OBJECT(client)); + g_object_unref(client); + } + else result = NULL; + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Fournit un accès à une définition de type à diffuser. * +* * +* Retour : Définition d'objet pour Python. * +* * +* Remarques : - * +* * +******************************************************************************/ + +PyTypeObject *get_python_admin_client_type(void) +{ + static PyMethodDef py_admin_client_methods[] = { + { NULL } + }; + + static PyGetSetDef py_admin_client_getseters[] = { + { NULL } + }; + + static PyTypeObject py_admin_client_type = { + + PyVarObject_HEAD_INIT(NULL, 0) + + .tp_name = "pychrysalide.analysis.db.AdminClient", + .tp_basicsize = sizeof(PyGObject), + + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + + .tp_doc = ADMIN_CLIENT_DOC, + + .tp_methods = py_admin_client_methods, + .tp_getset = py_admin_client_getseters, + .tp_new = py_admin_client_new, + + }; + + return &py_admin_client_type; + +} + + +/****************************************************************************** +* * +* Paramètres : module = module dont la définition est à compléter. * +* * +* Description : Prend en charge l'objet 'pychrysalide....db.AdminClient'. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool ensure_python_admin_client_is_registered(void) +{ + PyTypeObject *type; /* Type Python 'AdminClient' */ + PyObject *module; /* Module à recompléter */ + PyObject *dict; /* Dictionnaire du module */ + + type = get_python_admin_client_type(); + + if (!PyType_HasFeature(type, Py_TPFLAGS_READY)) + { + if (!ensure_python_hub_client_is_registered()) + return false; + + module = get_access_to_python_module("pychrysalide.analysis.db"); + + dict = PyModule_GetDict(module); + + if (!register_class_for_pygobject(dict, G_TYPE_ADMIN_CLIENT, type, get_python_hub_client_type())) + return false; + + } + + return true; + +} + + +/****************************************************************************** +* * +* Paramètres : arg = argument quelconque à tenter de convertir. * +* dst = destination des valeurs récupérées en cas de succès. * +* * +* Description : Tente de convertir en client administrateur. * +* * +* Retour : Bilan de l'opération, voire indications supplémentaires. * +* * +* Remarques : - * +* * +******************************************************************************/ + +int convert_to_admin_client(PyObject *arg, void *dst) +{ + int result; /* Bilan à retourner */ + + result = PyObject_IsInstance(arg, (PyObject *)get_python_admin_client_type()); + + switch (result) + { + case -1: + /* L'exception est déjà fixée par Python */ + result = 0; + break; + + case 0: + PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to admin client"); + break; + + case 1: + *((GAdminClient **)dst) = G_ADMIN_CLIENT(pygobject_get(arg)); + break; + + default: + assert(false); + break; + + } + + return result; + +} diff --git a/plugins/pychrysalide/analysis/db/admin.h b/plugins/pychrysalide/analysis/db/admin.h new file mode 100644 index 0000000..8a2dfeb --- /dev/null +++ b/plugins/pychrysalide/analysis/db/admin.h @@ -0,0 +1,45 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * admin.h - prototypes pour l'équivalent Python du fichier "analysis/db/admin.h" + * + * Copyright (C) 2019 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef _PLUGINS_PYCHRYSALIDE_ANALYSIS_DB_ADMIN_H +#define _PLUGINS_PYCHRYSALIDE_ANALYSIS_DB_ADMIN_H + + +#include +#include + + + +/* Fournit un accès à une définition de type à diffuser. */ +PyTypeObject *get_python_admin_client_type(void); + +/* Prend en charge l'objet 'pychrysalide.analysis.db.AdminClient'. */ +bool ensure_python_admin_client_is_registered(void); + +/* Tente de convertir en client administrateur. */ +int convert_to_admin_client(PyObject *, void *); + + + +#endif /* _PLUGINS_PYCHRYSALIDE_ANALYSIS_DB_ADMIN_H */ diff --git a/plugins/pychrysalide/analysis/db/analyst.c b/plugins/pychrysalide/analysis/db/analyst.c new file mode 100644 index 0000000..83e3878 --- /dev/null +++ b/plugins/pychrysalide/analysis/db/analyst.c @@ -0,0 +1,895 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * analyst.c - équivalent Python du fichier "analysis/db/analyst.c" + * + * Copyright (C) 2019 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "analyst.h" + + +#include +#include + + +#include +#include +#include + + +#include "client.h" +#include "collection.h" +#include "../../access.h" +#include "../../helpers.h" +#include "../../struct.h" + + + +/* Crée un nouvel objet Python de type 'AnalystClient'. */ +static PyObject *py_analyst_client_new(PyTypeObject *, PyObject *, PyObject *); + +/* Effectue une demande de sauvegarde de l'état courant. */ +static PyObject *py_analyst_client_save(PyObject *, PyObject *); + +/* Active les éléments en amont d'un horodatage donné. */ +static PyObject *py_analyst_client_set_last_active(PyObject *, PyObject *); + +/* Définit la désignation d'un instantané donné. */ +static PyObject *py_analyst_client_set_snapshot_name(PyObject *, PyObject *); + +/* Définit la désignation d'un instantané donné. */ +static PyObject *py_analyst_client_set_snapshot_desc(PyObject *, PyObject *); + +/* Restaure un ancien instantané. */ +static PyObject *py_analyst_client_restore_snapshot(PyObject *, PyObject *); + +/* Crée un nouvel instantané à partir d'un autre. */ +static PyObject *py_analyst_client_create_snapshot(PyObject *, PyObject *); + +/* Supprime un ancien instantané. */ +static PyObject *py_analyst_client_remove_snapshot(PyObject *, PyObject *); + +/* Fournit la liste des instantanés existants. */ +static PyObject *py_analyst_client_get_snapshots(PyObject *, void *); + +/* Fournit l'identifiant de l'instantané courant. */ +static PyObject *py_analyst_client_get_current_snapshot(PyObject *, void *); + + + +/****************************************************************************** +* * +* Paramètres : type = type de l'objet à instancier. * +* args = arguments fournis à l'appel. * +* kwds = arguments de type key=val fournis. * +* * +* Description : Crée un nouvel objet Python de type 'AnalystClient'. * +* * +* Retour : Instance Python mise en place. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_analyst_client_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyObject *result; /* Instance à retourner */ + const char *hash; /* Empreinte du binaire visé */ + PyObject *list; /* Liste Python de collections */ + int ret; /* Bilan de lecture des args. */ + Py_ssize_t length; /* Nombre d'éléments collectés */ + GList *collections; /* Liste native de collections */ + Py_ssize_t i; /* Boucle de parcours */ + PyObject *item; /* Elément de la liste Python */ + GDbCollection *collec; /* Version équivalente native */ + GAnalystClient *client; /* Serveur mis en place */ + +#define ANALYST_CLIENT_DOC \ + "AnalystClient provides and receives binary updates to and from a connected" \ + " to a server.\n" \ + "\n" \ + "Such clients must be authenticated and communications are encrypted using TLS.\n" \ + "\n" \ + "Instances can be created using the following constructor:\n" \ + "\n" \ + " AnalystClient(hash, list)" \ + "\n" \ + "Where hash is a SHA256 fingerprint of the studied binary and list is a list of" \ + " pychrysalide.analysis.db.DbCollection instances ; this kind of list can be" \ + " retrived with the pychrysalide.analysis.LoadedBinary.collections attribute." \ + "\n" \ + "AnalystClient 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." + + ret = PyArg_ParseTuple(args, "sO", &hash, &list); + if (!ret) return NULL; + + if (!PySequence_Check(list)) + { + PyErr_SetString(PyExc_TypeError, _("The second argument must be a collection list")); + return NULL; + } + + length = PySequence_Length(list); + + collections = NULL; + + for (i = 0; i < length; i++) + { + item = PySequence_GetItem(list, i); + + ret = convert_to_db_collection(item, &collec); + + Py_DECREF(item); + + if (ret != 1) + { + delete_collections_list(&collections); + result = NULL; + goto exit; + } + + g_object_ref(G_OBJECT(collec)); + collections = g_list_append(collections, collec); + + } + + client = g_analyst_client_new(hash, collections); + + if (client != NULL) + { + result = pygobject_new(G_OBJECT(client)); + g_object_unref(client); + } + else result = NULL; + + exit: + + 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. * +* * +* Retour : True si la commande a bien été envoyée, False sinon. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_analyst_client_save(PyObject *self, PyObject *args) +{ + PyObject *result; /* Bilan à retourner */ + GAnalystClient *client; /* Version native du serveur */ + bool status; /* Bilan de l'opération */ + +#define ANALYST_CLIENT_SAVE_METHOD PYTHON_METHOD_DEF \ +( \ + save, "$self, /", \ + METH_NOARGS, py_analyst_client, \ + "Ask the server for saving the current state of the analyzed binary" \ + " and returns the status of the request transmission." \ +) + + client = G_ANALYST_CLIENT(pygobject_get(self)); + + status = g_analyst_client_save(client); + + result = status ? Py_True : Py_False; + Py_INCREF(result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = client à manipuler. * +* args = arguments d'appel à consulter. * +* * +* Description : Active les éléments en amont d'un horodatage donné. * +* * +* Retour : True si la commande a bien été envoyée, False sinon. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_analyst_client_set_last_active(PyObject *self, PyObject *args) +{ + PyObject *result; /* Bilan à retourner */ + unsigned long long timestamp; /* Horodatage de limite */ + int ret; /* Bilan de lecture des args. */ + GAnalystClient *client; /* Version native du serveur */ + bool status; /* Bilan de l'opération */ + +#define ANALYST_CLIENT_SET_LAST_ACTIVE_METHOD PYTHON_METHOD_DEF \ +( \ + set_last_active, "$self, timestamp, /", \ + METH_VARARGS, py_analyst_client, \ + "Define the timestamp of the last active item in the collection" \ + " and returns the status of the request transmission." \ + "\n" \ + "This method should not be used directly. Prefer calling" \ + " pychrysalide.analysis.LoadedBinary.set_last_active() instead," \ + " as some items may be volatile and thus not handled by clients." \ +) + + ret = PyArg_ParseTuple(args, "K", ×tamp); + if (!ret) return NULL; + + client = G_ANALYST_CLIENT(pygobject_get(self)); + + status = g_analyst_client_set_last_active(client, timestamp); + + result = status ? Py_True : Py_False; + Py_INCREF(result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = client à manipuler. * +* args = arguments d'appel à consulter. * +* * +* Description : Définit la désignation d'un instantané donné. * +* * +* Retour : True si la commande a bien été envoyée, False sinon. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_analyst_client_set_snapshot_name(PyObject *self, PyObject *args) +{ + PyObject *result; /* Bilan à retourner */ + const char *raw_id; /* Identifiant brut */ + const char *text; /* Texte fourni à transmettre */ + int ret; /* Bilan de lecture des args. */ + snapshot_id_t id; /* Identifiant utilisable */ + bool status; /* Bilan d'opération */ + GAnalystClient *client; /* Version native du serveur */ + +#define ANALYST_CLIENT_SET_SNAPSHOT_NAME_METHOD PYTHON_METHOD_DEF \ +( \ + set_snapshot_name, "$self, id, name, /", \ + METH_VARARGS, py_analyst_client, \ + "Ask the server for defining a new name of for a snapshot using its" \ + " identifier and returns the status of the request transmission." \ + "\n" \ + "A 'snapshots-updated' signal is emitted once the request has been" \ + " processed with success." \ +) + + ret = PyArg_ParseTuple(args, "ss", &raw_id, &text); + if (!ret) return NULL; + + status = init_snapshot_id_from_text(&id, raw_id); + if (!status) + { + PyErr_SetString(PyExc_TypeError, _("provided value is not a valid snapshot identifier.")); + return NULL; + } + + client = G_ANALYST_CLIENT(pygobject_get(self)); + + status = g_analyst_client_set_snapshot_name(client, &id, text); + + result = status ? Py_True : Py_False; + Py_INCREF(result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = client à manipuler. * +* args = arguments d'appel à consulter. * +* * +* Description : Définit la désignation d'un instantané donné. * +* * +* Retour : True si la commande a bien été envoyée, False sinon. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_analyst_client_set_snapshot_desc(PyObject *self, PyObject *args) +{ + PyObject *result; /* Bilan à retourner */ + const char *raw_id; /* Identifiant brut */ + const char *text; /* Texte fourni à transmettre */ + int ret; /* Bilan de lecture des args. */ + snapshot_id_t id; /* Identifiant utilisable */ + bool status; /* Bilan d'opération */ + GAnalystClient *client; /* Version native du serveur */ + +#define ANALYST_CLIENT_SET_SNAPSHOT_DESC_METHOD PYTHON_METHOD_DEF \ +( \ + set_snapshot_desc, "$self, id, desc, /", \ + METH_VARARGS, py_analyst_client, \ + "Ask the server for defining a new description for a snapshot using" \ + " its identifier and returns the status of the request transmission." \ + "\n" \ + "A 'snapshots-updated' signal is emitted once the request has been" \ + " processed with success." \ +) + + ret = PyArg_ParseTuple(args, "ss", &raw_id, &text); + if (!ret) return NULL; + + status = init_snapshot_id_from_text(&id, raw_id); + if (!status) + { + PyErr_SetString(PyExc_TypeError, _("provided value is not a valid snapshot identifier.")); + return NULL; + } + + client = G_ANALYST_CLIENT(pygobject_get(self)); + + status = g_analyst_client_set_snapshot_desc(client, &id, text); + + result = status ? Py_True : Py_False; + Py_INCREF(result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = client à manipuler. * +* args = arguments d'appel à consulter. * +* * +* Description : Restaure un ancien instantané. * +* * +* Retour : True si la commande a bien été envoyée, False sinon. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_analyst_client_restore_snapshot(PyObject *self, PyObject *args) +{ + PyObject *result; /* Bilan à retourner */ + const char *raw_id; /* Identifiant brut */ + int ret; /* Bilan de lecture des args. */ + snapshot_id_t id; /* Identifiant utilisable */ + bool status; /* Bilan d'opération */ + GAnalystClient *client; /* Version native du serveur */ + +#define ANALYST_CLIENT_RESTORE_SNAPSHOT_METHOD PYTHON_METHOD_DEF \ +( \ + restore_snapshot, "$self, id, /", \ + METH_VARARGS, py_analyst_client, \ + "Ask the server for restoring a given snapshot using" \ + " its identifier and returns the status of the request transmission." \ + "\n" \ + "A 'snapshot-changed' signal is emitted once the request has been" \ + " processed with success." \ +) + + ret = PyArg_ParseTuple(args, "s", &raw_id); + if (!ret) return NULL; + + status = init_snapshot_id_from_text(&id, raw_id); + if (!status) + { + PyErr_SetString(PyExc_TypeError, _("provided value is not a valid snapshot identifier.")); + return NULL; + } + + client = G_ANALYST_CLIENT(pygobject_get(self)); + + status = g_analyst_client_restore_snapshot(client, &id); + + result = status ? Py_True : Py_False; + Py_INCREF(result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = client à manipuler. * +* args = arguments d'appel à consulter. * +* * +* Description : Crée un nouvel instantané à partir d'un autre. * +* * +* Retour : True si la commande a bien été envoyée, False sinon. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_analyst_client_create_snapshot(PyObject *self, PyObject *args) +{ + PyObject *result; /* Bilan à retourner */ + GAnalystClient *client; /* Version native du serveur */ + bool status; /* Bilan d'opération */ + +#define ANALYST_CLIENT_CREATE_SNAPSHOT_METHOD PYTHON_METHOD_DEF \ +( \ + create_snapshot, "$self, /", \ + METH_NOARGS, py_analyst_client, \ + "Ask the server for creating a new snapshot of the current state" \ + " and returns the status of the request transmission." \ + "\n" \ + "A 'snapshots-updated' signal is emitted once the request has been" \ + " processed with success." \ +) + + client = G_ANALYST_CLIENT(pygobject_get(self)); + + status = g_analyst_client_create_snapshot(client); + + result = status ? Py_True : Py_False; + Py_INCREF(result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = client à manipuler. * +* args = arguments d'appel à consulter. * +* * +* Description : Supprime un ancien instantané. * +* * +* Retour : True si la commande a bien été envoyée, False sinon. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_analyst_client_remove_snapshot(PyObject *self, PyObject *args) +{ + PyObject *result; /* Bilan à retourner */ + const char *raw_id; /* Identifiant brut */ + int rec; /* Indicateur de récursivité */ + int ret; /* Bilan de lecture des args. */ + snapshot_id_t id; /* Identifiant utilisable */ + bool status; /* Bilan d'opération */ + GAnalystClient *client; /* Version native du serveur */ + +#define ANALYST_CLIENT_REMOVE_SNAPSHOT_METHOD PYTHON_METHOD_DEF \ +( \ + remove_snapshot, "$self, id, recursive, /", \ + METH_VARARGS, py_analyst_client, \ + "Ask the server for removing a given snapshot using" \ + " its identifier and returns the status of the request transmission." \ + "\n" \ + "If this removal has not to be recursive, all children snapshots get" \ + " reassigned to the parent snapshot of the target." \ + "\n" \ + "A 'snapshots-updated' signal is emitted once the request has been" \ + " processed with success." \ +) + + ret = PyArg_ParseTuple(args, "sp", &raw_id, &rec); + if (!ret) return NULL; + + status = init_snapshot_id_from_text(&id, raw_id); + if (!status) + { + PyErr_SetString(PyExc_TypeError, _("provided value is not a valid snapshot identifier.")); + return NULL; + } + + client = G_ANALYST_CLIENT(pygobject_get(self)); + + status = g_analyst_client_remove_snapshot(client, &id, rec); + + 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 d'instantanés ou None. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_analyst_client_get_snapshots(PyObject *self, void *closure) +{ + PyObject *result; /* Valeur à retourner */ + GAnalystClient *client; /* Version native du serveur */ + snapshot_info_t *info; /* Liste d'instantanés présents*/ + size_t count; /* Taille de cette liste */ + bool status; /* Validité de cet identifiant */ + PyTypeObject *base; /* Modèle d'objet à créer */ + size_t i; /* Boucle de parcours */ + PyObject *item; /* Nouvelle description */ + char *text; /* Valeur textuelle à placer */ + PyObject *attrib; /* Attribut à constituer */ + int ret; /* Bilan d'une mise en place */ + bool failed; /* Détection d'une erreur */ + +#define ANALYST_CLIENT_SNAPSHOTS_ATTRIB PYTHON_GET_DEF_FULL \ +( \ + snapshots, py_analyst_client, \ + "List of all existing snapshots, provided as a tuple of pychrysalide.StructObject." \ + "\n" \ + "Each snapshot is characterised by the following properties :\n" \ + "* parent_id : identifier of the parent snapshot;\n" \ + "* id : identifier of the snapshot;\n" \ + "* created : timestamp of the creation date;\n" \ + "* name : name of the snapshot, or None;\n" \ + "* desc : description of the snapshot, or None." \ +) + + client = G_ANALYST_CLIENT(pygobject_get(self)); + + status = g_analyst_client_get_snapshots(client, &info, &count); + + if (status) + { + result = PyTuple_New(count); + + base = get_python_py_struct_type(); + + failed = false; + + for (i = 0; i < count; i++) + { + item = PyObject_CallFunction((PyObject *)base, NULL); + assert(item != NULL); + + text = snapshot_id_as_string(get_snapshot_info_parent_id(&info[i])); + attrib = PyUnicode_FromString(text); + ret = PyDict_SetItemString(item, "parent_id", attrib); + if (ret != 0) break; + + text = snapshot_id_as_string(get_snapshot_info_id(&info[i])); + attrib = PyUnicode_FromString(text); + ret = PyDict_SetItemString(item, "id", attrib); + if (ret != 0) break; + + attrib = PyLong_FromUnsignedLongLong(get_snapshot_info_created(&info[i])); + ret = PyDict_SetItemString(item, "created", attrib); + if (ret != 0) break; + + text = get_snapshot_info_name(&info[i]); + + if (text != NULL) + attrib = PyUnicode_FromString(text); + else + { + attrib = Py_None; + Py_INCREF(attrib); + } + + ret = PyDict_SetItemString(item, "name", attrib); + if (ret != 0) break; + + text = get_snapshot_info_desc(&info[i]); + + if (text != NULL) + attrib = PyUnicode_FromString(text); + else + { + attrib = Py_None; + Py_INCREF(attrib); + } + + ret = PyDict_SetItemString(item, "desc", attrib); + if (ret != 0) break; + + PyTuple_SetItem(result, i, item); + + } + + failed = (i < count); + + for (i = 0; i < count; i++) + exit_snapshot_info(&info[i]); + + free(info); + + if (failed) + goto on_failure; + + } + + else + { + result = Py_None; + Py_INCREF(result); + } + + return result; + + on_failure: + + Py_DECREF(result); + + return NULL; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet Python concerné par l'appel. * +* closure = non utilisé ici. * +* * +* Description : Fournit l'identifiant de l'instantané courant. * +* * +* Retour : Identifiant d'instantané ou None. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_analyst_client_get_current_snapshot(PyObject *self, void *closure) +{ + PyObject *result; /* Valeur à retourner */ + GAnalystClient *client; /* Version native du serveur */ + snapshot_id_t id; /* Identifiant à transmettre */ + bool status; /* Validité de cet identifiant */ + +#define ANALYST_CLIENT_CURRENT_SNAPSHOT_ATTRIB PYTHON_GETSET_DEF_FULL \ +( \ + current_snapshot, py_analyst_client, \ + "Identifier of the current snapshot, provided as a string." \ + "\n" \ + "The returned value is a cached version of the value stored at" \ + " server side. Thus, defining a new current snapshot is" \ + " successful as soon as the request to this server is sent." \ +) + + client = G_ANALYST_CLIENT(pygobject_get(self)); + + status = g_analyst_client_get_current_snapshot(client, &id); + + if (status) + result = PyUnicode_FromString(snapshot_id_as_string(&id)); + + else + { + result = Py_None; + Py_INCREF(result); + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet Python concerné par l'appel. * +* value = valeur fournie à intégrer ou prendre en compte. * +* closure = adresse non utilisée ici. * +* * +* Description : Définit l'identifiant de l'instantané courant. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static int py_analyst_client_set_current_snapshot(PyObject *self, PyObject *value, void *closure) +{ + int ret; /* Bilan d'analyse */ + void *raw; /* Valeur brute d'identifiant */ + snapshot_id_t id; /* Identifiant reconnu */ + bool status; /* Bilan d'une conversion */ + GAnalystClient *client; /* Version native du serveur */ + + ret = PyUnicode_Check(value); + if (!ret) return -1; + + raw = PyUnicode_DATA(value); + + status = init_snapshot_id_from_text(&id, raw); + if (!status) + { + PyErr_SetString(PyExc_TypeError, _("provided value is not a valid snapshot identifier.")); + return -1; + } + + client = G_ANALYST_CLIENT(pygobject_get(self)); + + status = g_analyst_client_set_current_snapshot(client, &id); + if (!status) + { + PyErr_SetString(PyExc_TypeError, "unable to send the provided snapshot identifier"); + return -1; + } + + return 0; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Fournit un accès à une définition de type à diffuser. * +* * +* Retour : Définition d'objet pour Python. * +* * +* Remarques : - * +* * +******************************************************************************/ + +PyTypeObject *get_python_analyst_client_type(void) +{ + static PyMethodDef py_analyst_client_methods[] = { + ANALYST_CLIENT_SAVE_METHOD, + ANALYST_CLIENT_SET_LAST_ACTIVE_METHOD, + ANALYST_CLIENT_SET_SNAPSHOT_NAME_METHOD, + ANALYST_CLIENT_SET_SNAPSHOT_DESC_METHOD, + ANALYST_CLIENT_RESTORE_SNAPSHOT_METHOD, + ANALYST_CLIENT_CREATE_SNAPSHOT_METHOD, + ANALYST_CLIENT_REMOVE_SNAPSHOT_METHOD, + { NULL } + }; + + static PyGetSetDef py_analyst_client_getseters[] = { + ANALYST_CLIENT_SNAPSHOTS_ATTRIB, + ANALYST_CLIENT_CURRENT_SNAPSHOT_ATTRIB, + { NULL } + }; + + static PyTypeObject py_analyst_client_type = { + + PyVarObject_HEAD_INIT(NULL, 0) + + .tp_name = "pychrysalide.analysis.db.AnalystClient", + .tp_basicsize = sizeof(PyGObject), + + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + + .tp_doc = ANALYST_CLIENT_DOC, + + .tp_methods = py_analyst_client_methods, + .tp_getset = py_analyst_client_getseters, + .tp_new = py_analyst_client_new, + + }; + + return &py_analyst_client_type; + +} + + +/****************************************************************************** +* * +* Paramètres : module = module dont la définition est à compléter. * +* * +* Description : Prend en charge l'objet 'pychrysalide....db.AnalystClient'. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool ensure_python_analyst_client_is_registered(void) +{ + PyTypeObject *type; /* Type Python 'AnalystClient' */ + PyObject *module; /* Module à recompléter */ + PyObject *dict; /* Dictionnaire du module */ + + type = get_python_analyst_client_type(); + + if (!PyType_HasFeature(type, Py_TPFLAGS_READY)) + { + if (!ensure_python_hub_client_is_registered()) + return false; + + module = get_access_to_python_module("pychrysalide.analysis.db"); + + dict = PyModule_GetDict(module); + + if (!register_class_for_pygobject(dict, G_TYPE_ANALYST_CLIENT, type, get_python_hub_client_type())) + return false; + + } + + return true; + +} + + +/****************************************************************************** +* * +* Paramètres : arg = argument quelconque à tenter de convertir. * +* dst = destination des valeurs récupérées en cas de succès. * +* * +* Description : Tente de convertir en client analyste. * +* * +* Retour : Bilan de l'opération, voire indications supplémentaires. * +* * +* Remarques : - * +* * +******************************************************************************/ + +int convert_to_analyst_client(PyObject *arg, void *dst) +{ + int result; /* Bilan à retourner */ + + result = PyObject_IsInstance(arg, (PyObject *)get_python_analyst_client_type()); + + switch (result) + { + case -1: + /* L'exception est déjà fixée par Python */ + result = 0; + break; + + case 0: + PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to analyst client"); + break; + + case 1: + *((GAnalystClient **)dst) = G_ANALYST_CLIENT(pygobject_get(arg)); + break; + + default: + assert(false); + break; + + } + + return result; + +} diff --git a/plugins/pychrysalide/analysis/db/analyst.h b/plugins/pychrysalide/analysis/db/analyst.h new file mode 100644 index 0000000..b250933 --- /dev/null +++ b/plugins/pychrysalide/analysis/db/analyst.h @@ -0,0 +1,45 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * analyst.h - prototypes pour l'équivalent Python du fichier "analysis/db/analyst.h" + * + * Copyright (C) 2019 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef _PLUGINS_PYCHRYSALIDE_ANALYSIS_DB_ANALYST_H +#define _PLUGINS_PYCHRYSALIDE_ANALYSIS_DB_ANALYST_H + + +#include +#include + + + +/* Fournit un accès à une définition de type à diffuser. */ +PyTypeObject *get_python_analyst_client_type(void); + +/* Prend en charge l'objet 'pychrysalide.analysis.db.AnalystClient'. */ +bool ensure_python_analyst_client_is_registered(void); + +/* Tente de convertir en client analyste. */ +int convert_to_analyst_client(PyObject *, void *); + + + +#endif /* _PLUGINS_PYCHRYSALIDE_ANALYSIS_DB_ANALYST_H */ diff --git a/plugins/pychrysalide/analysis/db/client.c b/plugins/pychrysalide/analysis/db/client.c index d5d8b48..0cd9704 100644 --- a/plugins/pychrysalide/analysis/db/client.c +++ b/plugins/pychrysalide/analysis/db/client.c @@ -41,84 +41,13 @@ -/* Crée un nouvel objet Python de type 'HubClient'. */ -static PyObject *py_hub_client_new(PyTypeObject *, PyObject *, PyObject *); - -/* Démarre la connexion à la base de données. */ -static PyObject *py_hub_client_start(PyObject *, PyObject *); - -/* Arrête la connexion à la base de données. */ -static PyObject *py_hub_client_stop(PyObject *, PyObject *); - -/* Effectue une demande de sauvegarde de l'état courant. */ -static PyObject *py_hub_client_save(PyObject *, PyObject *); - -/* Active les éléments en amont d'un horodatage donné. */ -static PyObject *py_hub_client_set_last_active(PyObject *, PyObject *); - -/* Définit la désignation d'un instantané donné. */ -static PyObject *py_hub_client_set_snapshot_name(PyObject *, PyObject *); - -/* Définit la désignation d'un instantané donné. */ -static PyObject *py_hub_client_set_snapshot_desc(PyObject *, PyObject *); - -/* Restaure un ancien instantané. */ -static PyObject *py_hub_client_restore_snapshot(PyObject *, PyObject *); - -/* Crée un nouvel instantané à partir d'un autre. */ -static PyObject *py_hub_client_create_snapshot(PyObject *, PyObject *); - -/* Supprime un ancien instantané. */ -static PyObject *py_hub_client_remove_snapshot(PyObject *, PyObject *); - -/* Fournit la liste des instantanés existants. */ -static PyObject *py_hub_client_get_snapshots(PyObject *, void *); - -/* Fournit l'identifiant de l'instantané courant. */ -static PyObject *py_hub_client_get_current_snapshot(PyObject *, void *); - - - -/****************************************************************************** -* * -* Paramètres : type = type de l'objet à instancier. * -* args = arguments fournis à l'appel. * -* kwds = arguments de type key=val fournis. * -* * -* Description : Crée un nouvel objet Python de type 'HubClient'. * -* * -* Retour : Instance Python mise en place. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static PyObject *py_hub_client_new(PyTypeObject *type, PyObject *args, PyObject *kwds) -{ - PyObject *result; /* Instance à retourner */ - const char *hash; /* Empreinte du binaire visé */ - PyObject *list; /* Liste Python de collections */ - int ret; /* Bilan de lecture des args. */ - Py_ssize_t length; /* Nombre d'éléments collectés */ - GList *collections; /* Liste native de collections */ - Py_ssize_t i; /* Boucle de parcours */ - PyObject *item; /* Elément de la liste Python */ - GDbCollection *collec; /* Version équivalente native */ - GHubClient *client; /* Serveur mis en place */ - #define HUB_CLIENT_DOC \ "HubClient provides and receives binary updates to and from a connected" \ " to a server.\n" \ "\n" \ "Such clients must be authenticated and communications are encrypted using TLS.\n" \ "\n" \ - "Instances can be created using the following constructor:\n" \ - "\n" \ - " HubClient(hash, list)" \ - "\n" \ - "Where hash is a SHA256 fingerprint of the studied binary and list is a list of" \ - " pychrysalide.analysis.db.DbCollection instances ; this kind of list can be" \ - " retrived with the pychrysalide.analysis.LoadedBinary.collections attribute." \ + "Instances can be created directly." \ "\n" \ "HubClient instances emit the following signals:\n" \ "* 'snapshots-updated'\n" \ @@ -132,53 +61,14 @@ static PyObject *py_hub_client_new(PyTypeObject *type, PyObject *args, PyObject " Handlers are expected to have only one argument: the client managing the" \ " snapshots." - ret = PyArg_ParseTuple(args, "sO", &hash, &list); - if (!ret) return NULL; - - if (!PySequence_Check(list)) - { - PyErr_SetString(PyExc_TypeError, _("The second argument must be a collection list")); - return NULL; - } - - length = PySequence_Length(list); - - collections = NULL; - - for (i = 0; i < length; i++) - { - item = PySequence_GetItem(list, i); - - ret = convert_to_db_collection(item, &collec); - - Py_DECREF(item); - if (ret != 1) - { - delete_collections_list(&collections); - result = NULL; - goto exit; - } - g_object_ref(G_OBJECT(collec)); - collections = g_list_append(collections, collec); - - } - - client = g_hub_client_new(hash, collections); - - if (client != NULL) - { - result = pygobject_new(G_OBJECT(client)); - g_object_unref(client); - } - else result = NULL; - - exit: +/* Démarre la connexion à la base de données. */ +static PyObject *py_hub_client_start(PyObject *, PyObject *); - return result; +/* Arrête la connexion à la base de données. */ +static PyObject *py_hub_client_stop(PyObject *, PyObject *); -} /****************************************************************************** @@ -275,588 +165,6 @@ static PyObject *py_hub_client_stop(PyObject *self, PyObject *args) /****************************************************************************** * * -* Paramètres : self = client à manipuler. * -* args = arguments d'appel non utilisés ici. * -* * -* Description : Effectue une demande de sauvegarde de l'état courant. * -* * -* Retour : True si la commande a bien été envoyée, False sinon. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static PyObject *py_hub_client_save(PyObject *self, PyObject *args) -{ - PyObject *result; /* Bilan à retourner */ - GHubClient *client; /* Version native du serveur */ - bool status; /* Bilan de l'opération */ - -#define HUB_CLIENT_SAVE_METHOD PYTHON_METHOD_DEF \ -( \ - save, "$self, /", \ - METH_NOARGS, py_hub_client, \ - "Ask the server for saving the current state of the analyzed binary" \ - " and returns the status of the request transmission." \ -) - - client = G_HUB_CLIENT(pygobject_get(self)); - - status = g_hub_client_save(client); - - result = status ? Py_True : Py_False; - Py_INCREF(result); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : self = client à manipuler. * -* args = arguments d'appel à consulter. * -* * -* Description : Active les éléments en amont d'un horodatage donné. * -* * -* Retour : True si la commande a bien été envoyée, False sinon. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static PyObject *py_hub_client_set_last_active(PyObject *self, PyObject *args) -{ - PyObject *result; /* Bilan à retourner */ - unsigned long long timestamp; /* Horodatage de limite */ - int ret; /* Bilan de lecture des args. */ - GHubClient *client; /* Version native du serveur */ - bool status; /* Bilan de l'opération */ - -#define HUB_CLIENT_SET_LAST_ACTIVE_METHOD PYTHON_METHOD_DEF \ -( \ - set_last_active, "$self, timestamp, /", \ - METH_VARARGS, py_hub_client, \ - "Define the timestamp of the last active item in the collection" \ - " and returns the status of the request transmission." \ - "\n" \ - "This method should not be used directly. Prefer calling" \ - " pychrysalide.analysis.LoadedBinary.set_last_active() instead," \ - " as some items may be volatile and thus not handled by clients." \ -) - - ret = PyArg_ParseTuple(args, "K", ×tamp); - if (!ret) return NULL; - - client = G_HUB_CLIENT(pygobject_get(self)); - - status = g_hub_client_set_last_active(client, timestamp); - - result = status ? Py_True : Py_False; - Py_INCREF(result); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : self = client à manipuler. * -* args = arguments d'appel à consulter. * -* * -* Description : Définit la désignation d'un instantané donné. * -* * -* Retour : True si la commande a bien été envoyée, False sinon. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static PyObject *py_hub_client_set_snapshot_name(PyObject *self, PyObject *args) -{ - PyObject *result; /* Bilan à retourner */ - const char *raw_id; /* Identifiant brut */ - const char *text; /* Texte fourni à transmettre */ - int ret; /* Bilan de lecture des args. */ - snapshot_id_t id; /* Identifiant utilisable */ - bool status; /* Bilan d'opération */ - GHubClient *client; /* Version native du serveur */ - -#define HUB_CLIENT_SET_SNAPSHOT_NAME_METHOD PYTHON_METHOD_DEF \ -( \ - set_snapshot_name, "$self, id, name, /", \ - METH_VARARGS, py_hub_client, \ - "Ask the server for defining a new name of for a snapshot using its" \ - " identifier and returns the status of the request transmission." \ - "\n" \ - "A 'snapshots-updated' signal is emitted once the request has been" \ - " processed with success." \ -) - - ret = PyArg_ParseTuple(args, "ss", &raw_id, &text); - if (!ret) return NULL; - - status = init_snapshot_id_from_text(&id, raw_id); - if (!status) - { - PyErr_SetString(PyExc_TypeError, _("provided value is not a valid snapshot identifier.")); - return NULL; - } - - client = G_HUB_CLIENT(pygobject_get(self)); - - status = g_hub_client_set_snapshot_name(client, &id, text); - - result = status ? Py_True : Py_False; - Py_INCREF(result); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : self = client à manipuler. * -* args = arguments d'appel à consulter. * -* * -* Description : Définit la désignation d'un instantané donné. * -* * -* Retour : True si la commande a bien été envoyée, False sinon. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static PyObject *py_hub_client_set_snapshot_desc(PyObject *self, PyObject *args) -{ - PyObject *result; /* Bilan à retourner */ - const char *raw_id; /* Identifiant brut */ - const char *text; /* Texte fourni à transmettre */ - int ret; /* Bilan de lecture des args. */ - snapshot_id_t id; /* Identifiant utilisable */ - bool status; /* Bilan d'opération */ - GHubClient *client; /* Version native du serveur */ - -#define HUB_CLIENT_SET_SNAPSHOT_DESC_METHOD PYTHON_METHOD_DEF \ -( \ - set_snapshot_desc, "$self, id, desc, /", \ - METH_VARARGS, py_hub_client, \ - "Ask the server for defining a new description for a snapshot using" \ - " its identifier and returns the status of the request transmission." \ - "\n" \ - "A 'snapshots-updated' signal is emitted once the request has been" \ - " processed with success." \ -) - - ret = PyArg_ParseTuple(args, "ss", &raw_id, &text); - if (!ret) return NULL; - - status = init_snapshot_id_from_text(&id, raw_id); - if (!status) - { - PyErr_SetString(PyExc_TypeError, _("provided value is not a valid snapshot identifier.")); - return NULL; - } - - client = G_HUB_CLIENT(pygobject_get(self)); - - status = g_hub_client_set_snapshot_desc(client, &id, text); - - result = status ? Py_True : Py_False; - Py_INCREF(result); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : self = client à manipuler. * -* args = arguments d'appel à consulter. * -* * -* Description : Restaure un ancien instantané. * -* * -* Retour : True si la commande a bien été envoyée, False sinon. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static PyObject *py_hub_client_restore_snapshot(PyObject *self, PyObject *args) -{ - PyObject *result; /* Bilan à retourner */ - const char *raw_id; /* Identifiant brut */ - int ret; /* Bilan de lecture des args. */ - snapshot_id_t id; /* Identifiant utilisable */ - bool status; /* Bilan d'opération */ - GHubClient *client; /* Version native du serveur */ - -#define HUB_CLIENT_RESTORE_SNAPSHOT_METHOD PYTHON_METHOD_DEF \ -( \ - restore_snapshot, "$self, id, /", \ - METH_VARARGS, py_hub_client, \ - "Ask the server for restoring a given snapshot using" \ - " its identifier and returns the status of the request transmission." \ - "\n" \ - "A 'snapshot-changed' signal is emitted once the request has been" \ - " processed with success." \ -) - - ret = PyArg_ParseTuple(args, "s", &raw_id); - if (!ret) return NULL; - - status = init_snapshot_id_from_text(&id, raw_id); - if (!status) - { - PyErr_SetString(PyExc_TypeError, _("provided value is not a valid snapshot identifier.")); - return NULL; - } - - client = G_HUB_CLIENT(pygobject_get(self)); - - status = g_hub_client_restore_snapshot(client, &id); - - result = status ? Py_True : Py_False; - Py_INCREF(result); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : self = client à manipuler. * -* args = arguments d'appel à consulter. * -* * -* Description : Crée un nouvel instantané à partir d'un autre. * -* * -* Retour : True si la commande a bien été envoyée, False sinon. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static PyObject *py_hub_client_create_snapshot(PyObject *self, PyObject *args) -{ - PyObject *result; /* Bilan à retourner */ - GHubClient *client; /* Version native du serveur */ - bool status; /* Bilan d'opération */ - -#define HUB_CLIENT_CREATE_SNAPSHOT_METHOD PYTHON_METHOD_DEF \ -( \ - create_snapshot, "$self, /", \ - METH_NOARGS, py_hub_client, \ - "Ask the server for creating a new snapshot of the current state" \ - " and returns the status of the request transmission." \ - "\n" \ - "A 'snapshots-updated' signal is emitted once the request has been" \ - " processed with success." \ -) - - client = G_HUB_CLIENT(pygobject_get(self)); - - status = g_hub_client_create_snapshot(client); - - result = status ? Py_True : Py_False; - Py_INCREF(result); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : self = client à manipuler. * -* args = arguments d'appel à consulter. * -* * -* Description : Supprime un ancien instantané. * -* * -* Retour : True si la commande a bien été envoyée, False sinon. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static PyObject *py_hub_client_remove_snapshot(PyObject *self, PyObject *args) -{ - PyObject *result; /* Bilan à retourner */ - const char *raw_id; /* Identifiant brut */ - int rec; /* Indicateur de récursivité */ - int ret; /* Bilan de lecture des args. */ - snapshot_id_t id; /* Identifiant utilisable */ - bool status; /* Bilan d'opération */ - GHubClient *client; /* Version native du serveur */ - -#define HUB_CLIENT_REMOVE_SNAPSHOT_METHOD PYTHON_METHOD_DEF \ -( \ - remove_snapshot, "$self, id, recursive, /", \ - METH_VARARGS, py_hub_client, \ - "Ask the server for removing a given snapshot using" \ - " its identifier and returns the status of the request transmission." \ - "\n" \ - "If this removal has not to be recursive, all children snapshots get" \ - " reassigned to the parent snapshot of the target." \ - "\n" \ - "A 'snapshots-updated' signal is emitted once the request has been" \ - " processed with success." \ -) - - ret = PyArg_ParseTuple(args, "sp", &raw_id, &rec); - if (!ret) return NULL; - - status = init_snapshot_id_from_text(&id, raw_id); - if (!status) - { - PyErr_SetString(PyExc_TypeError, _("provided value is not a valid snapshot identifier.")); - return NULL; - } - - client = G_HUB_CLIENT(pygobject_get(self)); - - status = g_hub_client_remove_snapshot(client, &id, rec); - - 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 d'instantanés ou None. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static PyObject *py_hub_client_get_snapshots(PyObject *self, void *closure) -{ - PyObject *result; /* Valeur à retourner */ - GHubClient *client; /* Version native du serveur */ - snapshot_info_t *info; /* Liste d'instantanés présents*/ - size_t count; /* Taille de cette liste */ - bool status; /* Validité de cet identifiant */ - PyTypeObject *base; /* Modèle d'objet à créer */ - size_t i; /* Boucle de parcours */ - PyObject *item; /* Nouvelle description */ - char *text; /* Valeur textuelle à placer */ - PyObject *attrib; /* Attribut à constituer */ - int ret; /* Bilan d'une mise en place */ - bool failed; /* Détection d'une erreur */ - -#define HUB_CLIENT_SNAPSHOTS_ATTRIB PYTHON_GET_DEF_FULL \ -( \ - snapshots, py_hub_client, \ - "List of all existing snapshots, provided as a tuple of pychrysalide.StructObject." \ - "\n" \ - "Each snapshot is characterised by the following properties :\n" \ - "* parent_id : identifier of the parent snapshot;\n" \ - "* id : identifier of the snapshot;\n" \ - "* created : timestamp of the creation date;\n" \ - "* name : name of the snapshot, or None;\n" \ - "* desc : description of the snapshot, or None." \ -) - - client = G_HUB_CLIENT(pygobject_get(self)); - - status = g_hub_client_get_snapshots(client, &info, &count); - - if (status) - { - result = PyTuple_New(count); - - base = get_python_py_struct_type(); - - failed = false; - - for (i = 0; i < count; i++) - { - item = PyObject_CallFunction((PyObject *)base, NULL); - assert(item != NULL); - - text = snapshot_id_as_string(get_snapshot_info_parent_id(&info[i])); - attrib = PyUnicode_FromString(text); - ret = PyDict_SetItemString(item, "parent_id", attrib); - if (ret != 0) break; - - text = snapshot_id_as_string(get_snapshot_info_id(&info[i])); - attrib = PyUnicode_FromString(text); - ret = PyDict_SetItemString(item, "id", attrib); - if (ret != 0) break; - - attrib = PyLong_FromUnsignedLongLong(get_snapshot_info_created(&info[i])); - ret = PyDict_SetItemString(item, "created", attrib); - if (ret != 0) break; - - text = get_snapshot_info_name(&info[i]); - - if (text != NULL) - attrib = PyUnicode_FromString(text); - else - { - attrib = Py_None; - Py_INCREF(attrib); - } - - ret = PyDict_SetItemString(item, "name", attrib); - if (ret != 0) break; - - text = get_snapshot_info_desc(&info[i]); - - if (text != NULL) - attrib = PyUnicode_FromString(text); - else - { - attrib = Py_None; - Py_INCREF(attrib); - } - - ret = PyDict_SetItemString(item, "desc", attrib); - if (ret != 0) break; - - PyTuple_SetItem(result, i, item); - - } - - failed = (i < count); - - for (i = 0; i < count; i++) - exit_snapshot_info(&info[i]); - - free(info); - - if (failed) - goto on_failure; - - } - - else - { - result = Py_None; - Py_INCREF(result); - } - - return result; - - on_failure: - - Py_DECREF(result); - - return NULL; - -} - - -/****************************************************************************** -* * -* Paramètres : self = objet Python concerné par l'appel. * -* closure = non utilisé ici. * -* * -* Description : Fournit l'identifiant de l'instantané courant. * -* * -* Retour : Identifiant d'instantané ou None. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static PyObject *py_hub_client_get_current_snapshot(PyObject *self, void *closure) -{ - PyObject *result; /* Valeur à retourner */ - GHubClient *client; /* Version native du serveur */ - snapshot_id_t id; /* Identifiant à transmettre */ - bool status; /* Validité de cet identifiant */ - -#define HUB_CLIENT_CURRENT_SNAPSHOT_ATTRIB PYTHON_GETSET_DEF_FULL \ -( \ - current_snapshot, py_hub_client, \ - "Identifier of the current snapshot, provided as a string." \ - "\n" \ - "The returned value is a cached version of the value stored at" \ - " server side. Thus, defining a new current snapshot is" \ - " successful as soon as the request to this server is sent." \ -) - - client = G_HUB_CLIENT(pygobject_get(self)); - - status = g_hub_client_get_current_snapshot(client, &id); - - if (status) - result = PyUnicode_FromString(snapshot_id_as_string(&id)); - - else - { - result = Py_None; - Py_INCREF(result); - } - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : self = objet Python concerné par l'appel. * -* value = valeur fournie à intégrer ou prendre en compte. * -* closure = adresse non utilisée ici. * -* * -* Description : Définit l'identifiant de l'instantané courant. * -* * -* Retour : Bilan de l'opération. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static int py_hub_client_set_current_snapshot(PyObject *self, PyObject *value, void *closure) -{ - int ret; /* Bilan d'analyse */ - void *raw; /* Valeur brute d'identifiant */ - snapshot_id_t id; /* Identifiant reconnu */ - bool status; /* Bilan d'une conversion */ - GHubClient *client; /* Version native du serveur */ - - ret = PyUnicode_Check(value); - if (!ret) return -1; - - raw = PyUnicode_DATA(value); - - status = init_snapshot_id_from_text(&id, raw); - if (!status) - { - PyErr_SetString(PyExc_TypeError, _("provided value is not a valid snapshot identifier.")); - return -1; - } - - client = G_HUB_CLIENT(pygobject_get(self)); - - status = g_hub_client_set_current_snapshot(client, &id); - if (!status) - { - PyErr_SetString(PyExc_TypeError, "unable to send the provided snapshot identifier"); - return -1; - } - - return 0; - -} - - -/****************************************************************************** -* * * Paramètres : - * * * * Description : Fournit un accès à une définition de type à diffuser. * @@ -872,19 +180,10 @@ PyTypeObject *get_python_hub_client_type(void) static PyMethodDef py_hub_client_methods[] = { HUB_CLIENT_START_METHOD, HUB_CLIENT_STOP_METHOD, - HUB_CLIENT_SAVE_METHOD, - HUB_CLIENT_SET_LAST_ACTIVE_METHOD, - HUB_CLIENT_SET_SNAPSHOT_NAME_METHOD, - HUB_CLIENT_SET_SNAPSHOT_DESC_METHOD, - HUB_CLIENT_RESTORE_SNAPSHOT_METHOD, - HUB_CLIENT_CREATE_SNAPSHOT_METHOD, - HUB_CLIENT_REMOVE_SNAPSHOT_METHOD, { NULL } }; static PyGetSetDef py_hub_client_getseters[] = { - HUB_CLIENT_SNAPSHOTS_ATTRIB, - HUB_CLIENT_CURRENT_SNAPSHOT_ATTRIB, { NULL } }; @@ -901,7 +200,7 @@ PyTypeObject *get_python_hub_client_type(void) .tp_methods = py_hub_client_methods, .tp_getset = py_hub_client_getseters, - .tp_new = py_hub_client_new, + .tp_new = no_python_constructor_allowed, }; diff --git a/plugins/pychrysalide/analysis/db/module.c b/plugins/pychrysalide/analysis/db/module.c index fa5a139..ddae5a7 100644 --- a/plugins/pychrysalide/analysis/db/module.c +++ b/plugins/pychrysalide/analysis/db/module.c @@ -28,6 +28,8 @@ #include +#include "admin.h" +#include "analyst.h" #include "certs.h" #include "client.h" #include "collection.h" @@ -98,6 +100,8 @@ bool populate_analysis_db_module(void) result = true; + if (result) result = ensure_python_admin_client_is_registered(); + if (result) result = ensure_python_analyst_client_is_registered(); if (result) result = ensure_python_certs_is_registered(); if (result) result = ensure_python_hub_client_is_registered(); if (result) result = ensure_python_db_collection_is_registered(); diff --git a/plugins/pychrysalide/analysis/db/server.c b/plugins/pychrysalide/analysis/db/server.c index 80ff4e2..9e4ee61 100644 --- a/plugins/pychrysalide/analysis/db/server.c +++ b/plugins/pychrysalide/analysis/db/server.c @@ -83,7 +83,8 @@ static PyObject *py_hub_server_new(PyTypeObject *type, PyObject *args, PyObject "\n" \ "Instances can be created using the following constructor:\n" \ "\n" \ - " HubServer(host=None, port='1337', ipv6=True)" \ + " HubServer()" \ + " HubServer(host='localhost', port='1337', ipv6=True)" \ "\n" \ "Where host and port define the listening properties of the server, and ipv6" \ " tries to establish IPv6 connections first." \ diff --git a/src/analysis/binary.c b/src/analysis/binary.c index 95d2133..19b46a2 100644 --- a/src/analysis/binary.c +++ b/src/analysis/binary.c @@ -37,7 +37,6 @@ #include "loaded-int.h" #include "routine.h" -#include "db/client.h" #include "disass/disassembler.h" #include "../arch/storage.h" #include "../common/extstr.h" @@ -73,8 +72,8 @@ struct _GLoadedBinary char *remote_host; /* Nom du serveur distant */ char *remote_port; /* Port du serveur distant */ - GHubClient *local; /* Enregistrements locaux */ - GHubClient *remote; /* Enregistrements distants */ + GAnalystClient *local; /* Enregistrements locaux */ + GAnalystClient *remote; /* Enregistrements distants */ DBStorage storages[DBF_COUNT]; /* Lieux d'enregistrement */ GList *collections; /* Ensemble de modifications */ @@ -830,9 +829,9 @@ static bool g_loaded_binary_connect_internal(GLoadedBinary *binary) /* Tentative de connexion */ - binary->local = g_hub_client_new(checksum, binary->collections); + binary->local = g_analyst_client_new(checksum, binary->collections); - result = g_hub_client_start_internal(binary->local); + result = g_hub_client_start_internal(G_HUB_CLIENT(binary->local)); return result; @@ -867,9 +866,10 @@ static bool g_loaded_binary_connect_remote(GLoadedBinary *binary) /* Tentative de connexion */ - binary->remote = g_hub_client_new(checksum, binary->collections); + binary->remote = g_analyst_client_new(checksum, binary->collections); - result = g_hub_client_start_remote(binary->local, binary->remote_host, binary->remote_port, true); + result = g_hub_client_start_remote(G_HUB_CLIENT(binary->remote), + binary->remote_host, binary->remote_port, true); if (!result) { @@ -951,9 +951,9 @@ bool g_loaded_binary_save_cache(const GLoadedBinary *binary) * * ******************************************************************************/ -GHubClient *g_loaded_binary_get_client(const GLoadedBinary *binary, bool internal) +GAnalystClient *g_loaded_binary_get_client(const GLoadedBinary *binary, bool internal) { - GHubClient *result; /* Instance à retourner */ + GAnalystClient *result; /* Instance à retourner */ if (internal) result = binary->local; @@ -1056,16 +1056,13 @@ bool g_loaded_binary_add_to_collection(GLoadedBinary *binary, GDbItem *item) bool result; /* Bilan à faire remonter */ DBFeatures feature; /* Domaine de fonctionnalité */ DBStorage storage; /* Forme d'enregistrement */ - GHubClient *client; /* Liaison à utiliser */ + GAnalystClient *client; /* Liaison à utiliser */ feature = g_db_item_get_feature(item); storage = g_loaded_binary_get_storage(binary, feature); - if (storage == DBS_ALL_REMOTE) - client = binary->remote; - else - client = binary->local; + client = g_loaded_binary_get_client(binary, storage == DBS_ALL_LOCAL); if (client == NULL) { @@ -1074,7 +1071,10 @@ bool g_loaded_binary_add_to_collection(GLoadedBinary *binary, GDbItem *item) } else - result = g_hub_client_add_item(client, item); + { + result = g_analyst_client_add_item(client, item); + g_object_unref(G_OBJECT(client)); + } g_object_unref(G_OBJECT(item)); @@ -1106,13 +1106,13 @@ bool g_loaded_binary_set_last_active(GLoadedBinary *binary, timestamp_t timestam if (binary->local != NULL) { - result = g_hub_client_set_last_active(binary->local, timestamp); + result = g_analyst_client_set_last_active(binary->local, timestamp); done = true; } if (result && binary->remote != NULL) { - result = g_hub_client_set_last_active(binary->remote, timestamp); + result = g_analyst_client_set_last_active(binary->remote, timestamp); done = true; } @@ -1546,7 +1546,7 @@ static bool g_loaded_binary_save(GLoadedBinary *binary, xmlDoc *xdoc, xmlXPathCo /* Sauvegarde côté serveur */ if (result) - g_hub_client_save(binary->local); + g_analyst_client_save(binary->local); return result; diff --git a/src/analysis/binary.h b/src/analysis/binary.h index 194ccd0..56f21a1 100644 --- a/src/analysis/binary.h +++ b/src/analysis/binary.h @@ -31,7 +31,7 @@ #include "content.h" #include "loaded.h" #include "db/collection.h" -#include "db/client.h" +#include "db/analyst.h" #include "db/protocol.h" #include "../arch/processor.h" #include "../format/debuggable.h" @@ -114,7 +114,7 @@ bool g_loaded_binary_save_cache(const GLoadedBinary *); /* Fournit un client assurant la liaison avec un serveur. */ -GHubClient *g_loaded_binary_get_client(const GLoadedBinary *, bool); +GAnalystClient *g_loaded_binary_get_client(const GLoadedBinary *, bool); /* Fournit l'ensemble des collections utilisées par un binaire. */ GDbCollection **g_loaded_binary_get_collections(const GLoadedBinary *, size_t *); diff --git a/src/analysis/db/Makefile.am b/src/analysis/db/Makefile.am index 3edfb09..8f8242c 100644 --- a/src/analysis/db/Makefile.am +++ b/src/analysis/db/Makefile.am @@ -3,12 +3,18 @@ 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 \ diff --git a/src/analysis/db/admin.c b/src/analysis/db/admin.c new file mode 100644 index 0000000..355f8c0 --- /dev/null +++ b/src/analysis/db/admin.c @@ -0,0 +1,292 @@ + +/* 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 . + */ + + +#include "admin.h" + + +#include +#include + + +#include "client-int.h" +#include "../../core/logs.h" + + + +/* Description de client à l'écoute (instance) */ +struct _GAdminClient +{ + GHubClient parent; /* A laisser en premier */ + +}; + +/* Description de client à l'écoute (classe) */ +struct _GAdminClientClass +{ + GHubClientClass parent; /* A laisser en premier */ + +}; + + +/* 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 *); + + + +/* 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; + +} + + +/****************************************************************************** +* * +* Paramètres : client = instance à initialiser. * +* * +* Description : Initialise une description de fichier binaire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_admin_client_init(GAdminClient *client) +{ + +} + + +/****************************************************************************** +* * +* 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) +{ + 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 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 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) + { + 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; + +} diff --git a/src/analysis/db/admin.h b/src/analysis/db/admin.h new file mode 100644 index 0000000..f5a6b5d --- /dev/null +++ b/src/analysis/db/admin.h @@ -0,0 +1,62 @@ + +/* 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 . + */ + + +#ifndef _ANALYSIS_DB_ADMIN_H +#define _ANALYSIS_DB_ADMIN_H + + +#include +#include +#include + + +#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); + + + +#endif /* _ANALYSIS_DB_ADMIN_H */ diff --git a/src/analysis/db/analyst.c b/src/analysis/db/analyst.c new file mode 100644 index 0000000..a828c11 --- /dev/null +++ b/src/analysis/db/analyst.c @@ -0,0 +1,1136 @@ + +/* 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 . + */ + + +#include "analyst.h" + + +#include +#include + + +#include "client-int.h" +#include "../../core/logs.h" + + + +/* Description de client à l'écoute (instance) */ +struct _GAnalystClient +{ + GHubClient parent; /* A laisser en premier */ + + rle_string hash; /* Empreinte du binaire lié */ + 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 (* snapshots_updated) (GAnalystClient *); + void (* snapshot_changed) (GAnalystClient *); + +}; + + +/* 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 *); + +/* 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 *); + +/* Met à jour l'identifiant de l'instantané courant. */ +static bool g_analyst_client_update_current_snapshot(GAnalystClient *, packed_buffer *); + + + +/* 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->recv_func = (GThreadFunc)g_analyst_client_update; + + 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) +{ + setup_empty_rle_string(&client->hash); + 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_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 */ + + unset_rle_string(&client->hash); + + 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. * +* 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 : - * +* * +******************************************************************************/ + +GAnalystClient *g_analyst_client_new(const char *hash, GList *collections) +{ + GAnalystClient *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 : 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 out_pbuf; /* Tampon d'émission */ + bool status; /* Bilan d'une opération */ + struct pollfd fds[2]; /* 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 */ + + 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_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_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 *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 *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 : 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 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 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 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 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 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 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 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 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 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; + +} diff --git a/src/analysis/db/analyst.h b/src/analysis/db/analyst.h new file mode 100644 index 0000000..d9e90c6 --- /dev/null +++ b/src/analysis/db/analyst.h @@ -0,0 +1,95 @@ + +/* 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 . + */ + + +#ifndef _ANALYSIS_DB_ANALYST_H +#define _ANALYSIS_DB_ANALYST_H + + +#include +#include +#include + + +#include "client.h" +#include "collection.h" +#include "misc/snapshot.h" + + + +#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); + +/* Prépare un client pour une connexion à une BD. */ +GAnalystClient *g_analyst_client_new(const char *, GList *); + +/* 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/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 . + */ + + +#ifndef _ANALYSIS_DB_BACKEND_INT_H +#define _ANALYSIS_DB_BACKEND_INT_H + + +#include + + +#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 . + */ + + +#include "backend.h" + + +#include + + +#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 . + */ + + +#ifndef _ANALYSIS_DB_BACKEND_H +#define _ANALYSIS_DB_BACKEND_H + + +#include +#include + + + +#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..8d589b3 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 * @@ -29,7 +29,6 @@ #include #include #include -#include #include #include #include @@ -42,6 +41,7 @@ #include +#include "backend-int.h" #include "collection.h" #include "protocol.h" #include "snapshot.h" @@ -61,7 +61,8 @@ /* 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 */ @@ -89,11 +90,6 @@ struct _GCdbArchive 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) */ @@ -147,6 +143,15 @@ 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 *); @@ -156,12 +161,6 @@ static bool g_cdb_archive_send_snapshot_update(GCdbArchive *, packed_buffer *); /* Envoie à tous les clients le nouvel instantané courant. */ static bool g_cdb_archive_send_snapshot_change(GCdbArchive *, packed_buffer *); -/* 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); - /* ---------------------------------------------------------------------------------- */ @@ -170,7 +169,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 +187,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; + } @@ -213,6 +220,7 @@ static void g_cdb_archive_init(GCdbArchive *archive) { setup_empty_rle_string(&archive->hash); + archive->filename = NULL; archive->tmpdir = NULL; archive->collections = create_collections_list(); @@ -221,9 +229,6 @@ static void g_cdb_archive_init(GCdbArchive *archive) g_mutex_init(&archive->clients_access); - g_mutex_init(&archive->id_access); - g_cond_init(&archive->id_cond); - } @@ -241,10 +246,9 @@ static void g_cdb_archive_init(GCdbArchive *archive) static void g_cdb_archive_dispose(GCdbArchive *archive) { - g_clear_object(&archive->snapshot); + g_server_backend_stop(G_SERVER_BACKEND(archive)); - g_cond_clear(&archive->id_cond); - g_mutex_clear(&archive->id_access); + g_clear_object(&archive->snapshot); g_mutex_clear(&archive->clients_access); @@ -284,7 +288,8 @@ static void g_cdb_archive_finalize(GCdbArchive *archive) if (archive->xml_desc != NULL) free(archive->xml_desc); - free(archive->filename); + if (archive->filename != NULL) + free(archive->filename); if (archive->tmpdir != NULL) free(archive->tmpdir); @@ -804,7 +809,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,6 +837,7 @@ static void on_collection_extended(GDbCollection *collec, GDbItem *item, GCdbArc static void *g_cdb_archive_process(GCdbArchive *archive) { + GServerBackend *base; /* Base de l'instance */ struct pollfd *fds; /* Surveillance des flux */ nfds_t nfds; /* Quantité de ces flux */ nfds_t i; /* Boucle de parcours */ @@ -846,14 +852,7 @@ static void *g_cdb_archive_process(GCdbArchive *archive) bool reload; /* Besoin de rechargement */ char *msg; /* Erreur à faire remonter */ - void interrupt_poll_with_sigusr1(int sig) { }; - - 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); + base = G_SERVER_BACKEND(archive); fds = NULL; @@ -863,35 +862,46 @@ static void *g_cdb_archive_process(GCdbArchive *archive) g_mutex_lock(&archive->clients_access); - nfds = archive->count; + nfds = archive->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(archive->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 +920,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, archive->clients[i].tls_fd); if (!status) goto gcap_bad_exchange; next_command: @@ -935,7 +945,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, archive->clients[i].tls_fd); if (!status) goto gcap_bad_reply; exit_packed_buffer(&out_pbuf); @@ -976,7 +986,7 @@ 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); + status = ssl_send_packed_buffer(&out_pbuf, archive->clients[i].tls_fd); if (!status) goto gcap_bad_reply; exit_packed_buffer(&out_pbuf); @@ -998,7 +1008,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, archive->clients[i].tls_fd); if (!status) goto gcap_bad_reply; exit_packed_buffer(&out_pbuf); @@ -1010,7 +1020,7 @@ 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); + status = ssl_send_packed_buffer(&out_pbuf, archive->clients[i].tls_fd); if (!status) goto gcap_bad_reply; exit_packed_buffer(&out_pbuf); @@ -1022,7 +1032,7 @@ 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); + status = ssl_send_packed_buffer(&out_pbuf, archive->clients[i].tls_fd); if (!status) goto gcap_bad_reply; exit_packed_buffer(&out_pbuf); @@ -1185,12 +1195,7 @@ static void *g_cdb_archive_process(GCdbArchive *archive) gcap_no_more_clients: - archive->process = NULL; - - g_mutex_lock(&archive->id_access); - archive->process_id = 0; - g_cond_signal(&archive->id_cond); - g_mutex_unlock(&archive->id_access); + g_server_backend_stop(G_SERVER_BACKEND(archive)); if (fds != NULL) free(fds); @@ -1202,6 +1207,115 @@ 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 void g_cdb_archive_add_client(GCdbArchive *archive, SSL *fd, const char *peer_name, const char *user) +{ + cdb_client *client; /* Nouvelle fiche d'entité */ + + /** + * 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 que 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. + */ + + g_mutex_lock(&archive->clients_access); + + archive->clients = realloc(archive->clients, ++archive->count * sizeof(cdb_client)); + + client = &archive->clients[archive->count - 1]; + + client->tls_fd = fd; + client->peer_name = strdup(peer_name); + client->user = strdup(user); + + g_mutex_unlock(&archive->clients_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) +{ + cdb_client *client; /* Client à traiter */ + + assert(!g_mutex_trylock(&archive->clients_access)); + + client = &archive->clients[index]; + + SSL_free(client->tls_fd); + free(client->user); + + 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. * * * @@ -1222,7 +1336,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); @@ -1337,134 +1451,3 @@ static bool g_cdb_archive_send_snapshot_change(GCdbArchive *archive, packed_buff return result; } - - -/****************************************************************************** -* * -* Paramètres : archive = archive à connecter avec un utilisateur. * -* fd = canal de communication réseau ouvert. * -* * -* Description : Associe un nouvel utilisateur à l'archive. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -void g_cdb_archive_add_client(GCdbArchive *archive, SSL *fd) -{ - X509 *peer_cert; /* Certificat présenté */ - volatile pthread_t *process_id; /* Identifiant de la procédure */ - - /** - * 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. - */ - - /* Ajout dans la liste officielle */ - - g_mutex_lock(&archive->clients_access); - - archive->clients = realloc(archive->clients, ++archive->count * sizeof(cdb_client)); - - 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); - - X509_free(peer_cert); - - /* Démarrage ou redémarrage du processus d'écoute */ - - if (archive->process == NULL) - { - archive->process = g_thread_new("cdb_process", (GThreadFunc)g_cdb_archive_process, archive); - - /* On attend que le processus parallèle soit prêt */ - - process_id = &archive->process_id; - - 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); - - } - else - pthread_kill(archive->process_id, SIGUSR1); - - g_mutex_unlock(&archive->clients_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) -{ - cdb_client *client; /* Client à traiter */ - - assert(!g_mutex_trylock(&archive->clients_access)); - - client = &archive->clients[index]; - - SSL_free(client->ssl_fd); - free(client->user); - - 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); - -} diff --git a/src/analysis/db/cdb.h b/src/analysis/db/cdb.h index 58bb781..2dd8118 100644 --- a/src/analysis/db/cdb.h +++ b/src/analysis/db/cdb.h @@ -64,13 +64,4 @@ 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 *); - - - - - #endif /* _ANALYSIS_DB_CDB_H */ diff --git a/src/analysis/db/client-int.h b/src/analysis/db/client-int.h new file mode 100644 index 0000000..83c5039 --- /dev/null +++ b/src/analysis/db/client-int.h @@ -0,0 +1,71 @@ + +/* 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 . + */ + + +#ifndef _ANALYSIS_DB_CLIENT_INT_H +#define _ANALYSIS_DB_CLIENT_INT_H + + +#include "client.h" + + + +/* 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 */ + 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..1f47eea 100644 --- a/src/analysis/db/client.c +++ b/src/analysis/db/client.c @@ -37,6 +37,7 @@ #include +#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,22 +99,6 @@ 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); - } @@ -190,9 +116,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 +125,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 +146,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 +167,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 +176,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 +183,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,6 +392,7 @@ 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 */ + GHubClientClass *class; /* Classe du client connecté */ packed_buffer out_pbuf; /* Tampon d'émission */ bool status; /* Bilan d'une opération */ packed_buffer in_pbuf; /* Tampon de réception */ @@ -608,11 +479,13 @@ 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. * * Tout ceci est à synchroniser avec la fonction g_db_server_listener(). */ @@ -625,15 +498,19 @@ 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; + + + + 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 +566,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 +586,7 @@ static bool g_hub_client_start_common(GHubClient *client, char *desc) return true; + sys_error: comm_error: exit_packed_buffer(&in_pbuf); @@ -733,296 +616,68 @@ 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. * +* Description : Arrête la connexion à la base de données. * * * -* Retour : NULL. * +* Retour : - * * * * Remarques : - * * * ******************************************************************************/ -static void *g_hub_client_update(GHubClient *client) +void g_hub_client_stop(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 fd; /* Canal à clôturer */ 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); + ssize_t sent; /* Quantité de données émises */ - /** - * Phase d'écoute continue... - */ - - fds.fd = client->fd; - fds.events = POLLIN | POLLPRI; - - init_packed_buffer(&in_pbuf); + /* Gestion du double appel */ - while (client->fd != -1) + if (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 + * Si la fermture est forcée, le thread de traitement va terminer en erreur. + * Donc cette fonction sera appelée deux fois. Seule la première va affecter + * le contexte, donc on le peut pas s'assurer de la condition suivante dans + * tous les cas. */ - 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; - - } - + /*assert(client->tls_ctx == NULL);*/ + return; } - 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 */ + fd = client->fd; - result = true; + client->fd = -1; - g_mutex_lock(&client->snap_lock); + /* Ordre d'arrêt */ - if (client->snapshots != NULL) + if (g_thread_self() != client->update) { - for (i = 0; i < client->snap_count; i++) - exit_snapshot_info(&client->snapshots[i]); + sent = write(client->stop_ctrl[1], "\xf0", 1); + if (sent != 1) LOG_ERROR_N("write"); - free(client->snapshots); - - client->snapshots = NULL; - client->snap_count = 0; + g_thread_join(client->update); } - 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); + /* Fermeture des flux */ - exit_snapshot_info(&info); - - } + SSL_free(client->tls_fd); + client->tls_fd = NULL; - } - while (true); + SSL_CTX_free(client->tls_ctx); + client->tls_ctx = NULL; - g_mutex_unlock(&client->snap_lock); + ret = close(fd); + if (ret == -1) LOG_ERROR_N("close"); - if (result) - g_signal_emit_by_name(client, "snapshots-updated"); + ret = close(client->stop_ctrl[0]); + if (ret == -1) LOG_ERROR_N("close"); + client->stop_ctrl[0] = -1; - return result; + ret = close(client->stop_ctrl[1]); + if (ret == -1) LOG_ERROR_N("close"); + client->stop_ctrl[1] = -1; } @@ -1030,36 +685,37 @@ static bool g_hub_client_update_snapshots(GHubClient *client, packed_buffer *pbu /****************************************************************************** * * * 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. * +* Description : Identifie le canal de communication pour envois au serveur. * * * -* Retour : true si l'opération s'est déroulée sans encombre, ou false. * +* Retour : Descripteur de flux normalement ouvert. * * * * Remarques : - * * * ******************************************************************************/ -static bool g_hub_client_update_current_snapshot(GHubClient *client, packed_buffer *pbuf) +SSL *g_hub_client_get_ssl_fd(GHubClient *client) { - 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); + SSL *result; /* Canal à retourner */ +#ifndef NDEBUG + int ret; /* Validation de transmission */ +#endif - copy_snapshot_id(&client->current, &id); - client->has_current = true; + g_mutex_lock(&client->sending_lock); - g_mutex_unlock(&client->cur_lock); + result = client->tls_fd; - g_signal_emit_by_name(client, "snapshot-changed"); + if (result == NULL) + g_mutex_unlock(&client->sending_lock); + else + { +#ifndef NDEBUG + ret = SSL_up_ref(result); + assert(ret == 1); +#else + SSL_up_ref(result); +#endif } return result; @@ -1070,8 +726,9 @@ static bool g_hub_client_update_current_snapshot(GHubClient *client, packed_buff /****************************************************************************** * * * Paramètres : client = client pour les accès distants à manipuler. * +* tls_fd = canal de communication SSL. * * * -* Description : Arrête la connexion à la base de données. * +* Description : Marque le canal de communication comme disponible. * * * * Retour : - * * * @@ -1079,636 +736,10 @@ static bool g_hub_client_update_current_snapshot(GHubClient *client, packed_buff * * ******************************************************************************/ -void g_hub_client_stop(GHubClient *client) +void g_hub_client_put_ssl_fd(GHubClient *client, SSL *tls_fd) { - int fd; /* Canal à clôturer */ - int ret; /* Bilan d'un appel */ - - /* Canal de communication */ - - if (client->fd == -1) - { - /** - * Si la fermture est forcée, le thread de traitement va terminer en erreur. - * Donc cette fonction sera appelée deux fois. Seule la première va affecter - * le contexte, donc on le peut pas s'assurer de la condition suivante dans - * tous les cas. - */ - - /*assert(client->tls_ctx == NULL);*/ - return; - } - - fd = client->fd; - - client->fd = -1; - - ret = close(fd); - if (ret == -1) LOG_ERROR_N("close"); - - if (g_thread_self() != client->update) - g_thread_join(client->update); - - /* Environnement TLS */ - - SSL_free(client->tls_fd); - client->tls_fd = NULL; - - SSL_CTX_free(client->tls_ctx); - client->tls_ctx = NULL; - -} - - -/****************************************************************************** -* * -* Paramètres : client = client pour les accès distants à manipuler. * -* * -* Description : Identifie le canal de communication pour envois au serveur. * -* * -* Retour : Descripteur de flux normalement ouvert. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static SSL *g_hub_client_get_ssl_fd(GHubClient *client) -{ - SSL *result; /* Canal à retourner */ -#ifndef NDEBUG - int ret; /* Validation de transmission */ -#endif - - g_mutex_lock(&client->sending_lock); - - result = client->tls_fd; - - if (result == NULL) - g_mutex_unlock(&client->sending_lock); - - else - { -#ifndef NDEBUG - ret = SSL_up_ref(result); - assert(ret == 1); -#else - SSL_up_ref(result); -#endif - } - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : client = client pour les accès distants à manipuler. * -* tls_fd = canal de communication SSL. * -* * -* Description : Marque le canal de communication comme disponible. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_hub_client_put_ssl_fd(GHubClient *client, SSL *tls_fd) -{ - g_mutex_unlock(&client->sending_lock); + 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 -#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/controller.c b/src/analysis/db/controller.c new file mode 100644 index 0000000..46b628d --- /dev/null +++ b/src/analysis/db/controller.c @@ -0,0 +1,449 @@ + +/* 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 . + */ + + +#include "controller.h" + + +#include +#include +#include +#include +#include +#include + + +#include + + +#include "backend-int.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 *); + + + +/* 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 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 */ + 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_ARCHIVES: + + /* + error = g_cdb_controller_write(archive); + + 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 []) { error }, sizeof(uint32_t), true); + 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); + +} + + + + + +#if 0 +/****************************************************************************** +* * +* Paramètres : controller = archive à connecter avec un utilisateur. * +* pbuf = paquet à consituer pour un envoi unique. [OUT] * +* * +* Description : Envoie à tous les clients la nouvelle liste d'instantanés. * +* * +* Retour : Bilan de constitution de la réponse. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool g_cdb_controller_send_snapshot_update(GCdbController *controller, packed_buffer *pbuf) +{ + bool result; /* Bilan à retourner */ + bool do_send; /* Réalisation de l'émission */ + packed_buffer out_pbuf; /* Tampon d'émission */ + + do_send = (pbuf == NULL); + + if (pbuf == NULL) + pbuf = &out_pbuf; + + init_packed_buffer(pbuf); + + result = extend_packed_buffer(pbuf, (uint32_t []) { DBC_SNAPSHOTS_UPDATED }, + sizeof(uint32_t), true); + if (!result) goto bad_reply; + + result = g_db_snapshot_pack_all(archive->snapshot, pbuf); + if (!result) goto bad_reply; + + result = extend_packed_buffer(pbuf, SNAPSHOT_END_MARK, SNAP_ID_HEX_SZ, false); + if (!result) goto bad_reply; + + bad_reply: + + if (do_send || !result) + exit_packed_buffer(pbuf); + + return result; + +} + +#endif 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 . + */ + + +#ifndef _ANALYSIS_DB_CONTROLLER_H +#define _ANALYSIS_DB_CONTROLLER_H + + +#include +#include + + +#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/protocol.h b/src/analysis/db/protocol.h index f673c4b..41e3ae7 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 + */ @@ -58,7 +62,14 @@ typedef enum _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; @@ -114,9 +125,38 @@ 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 ] + * [ 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 ------------------------ */ + + + + DBC_LIST_ARCHIVES, /* Fourniture des identifiants */ + + + + + /* ------------------------ Commandes pour analyste ------------------------ */ + /** * Gestion de la commande 'DBC_SAVE'. * diff --git a/src/analysis/db/server.c b/src/analysis/db/server.c index 258a66c..ad7929f 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 *, const char *, DBError *, bool *); + +/* Assure l'accueil des nouveaux clients analystes. */ +static GServerBackend *g_hub_server_handle_analyst(GHubServer *, packed_buffer *, 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,18 +748,16 @@ 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 */ 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 */ + uint32_t role; /* Rôle visé par le client */ + bool new; /* Besoin d'ajout à une liste */ packed_buffer out_pbuf; /* Tampon d'émission */ fds.fd = server->fd; @@ -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); - - 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_ADMIN: + backend = g_hub_server_handle_admin(server, &in_pbuf, peer_name, &error, &new); break; - } - if (iter == NULL) - { - basedir = strdup(server->working); - basedir = stradd(basedir, "cdbs" G_DIR_SEPARATOR_S); + case CRL_ANALYST: + backend = g_hub_server_handle_analyst(server, &in_pbuf, peer_name, &error, &new); + break; - tmpdir = strdup(server->working); - tmpdir = stradd(tmpdir, "tmp" G_DIR_SEPARATOR_S); + default: + log_variadic_message(LMT_ERROR, _("Unknown client role requested by '%s'"), + peer_name); + backend = NULL; + error = DBE_BAD_EXCHANGE; + new = false; + break; - archive = g_cdb_archive_new(basedir, tmpdir, &hash, &error); + } - free(tmpdir); - free(basedir); + assert((backend == NULL && error != DBE_NONE) || (backend != NULL && error == DBE_NONE)); - } + 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); + if (new) + g_hub_server_register_backend(server, backend); - exit_packed_buffer(&out_pbuf); - - free(peer_name); - - 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,261 @@ 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 *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 to 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 *in_pbuf, const char *peer_name, DBError *error, bool *new) +{ + GCdbArchive *result; /* Support de suivi à retourner*/ + rle_string hash; /* Empreinte du binaire 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 */ + + 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; + } + + if (has_more_data_in_packed_buffer(in_pbuf)) + { + log_variadic_message(LMT_ERROR, _("The client from '%s' provided to much data!"), peer_name); + goto wrong_receiving; + } + + /* 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_hash(archive, &hash) == 0) + 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, error); + + free(tmpdir); + free(basedir); + + *new = true; + + } + + wrong_receiving: + + 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/gui/dialogs/snapshots.c b/src/gui/dialogs/snapshots.c index 8abbdf2..53fc7b0 100644 --- a/src/gui/dialogs/snapshots.c +++ b/src/gui/dialogs/snapshots.c @@ -60,7 +60,7 @@ static void on_server_selection_changed(GtkComboBox *, GtkBuilder *); static bool find_suitable_parent(GtkTreeModel *, GtkTreeIter *, const char *, GtkTreeIter *); /* Met à jour l'affichage avec une nouvelle liste d'instantanés. */ -static void on_snapshots_updated(GHubClient *, GtkBuilder *); +static void on_snapshots_updated(GAnalystClient *, GtkBuilder *); /* Réinitialise la zone d'affichage des informations. */ static void reset_information_area(GtkBuilder *); @@ -181,11 +181,11 @@ GtkWidget *create_snapshots_dialog(GLoadedBinary *binary, GtkWindow *parent, Gtk static void on_dialog_destroy(GtkWidget *dialog, GtkBuilder *builder) { - GHubClient *client; /* Cible des interactions */ + GAnalystClient *client; /* Cible des interactions */ /* Déconnexion de l'ancien */ - client = G_HUB_CLIENT(g_object_get_data(G_OBJECT(builder), "client")); + client = G_ANALYST_CLIENT(g_object_get_data(G_OBJECT(builder), "client")); if (client != NULL) g_signal_handlers_disconnect_by_func(client, on_snapshots_updated, builder); @@ -208,14 +208,14 @@ static void on_dialog_destroy(GtkWidget *dialog, GtkBuilder *builder) static void on_server_selection_changed(GtkComboBox *combo, GtkBuilder *builder) { - GHubClient *client; /* Cible des interactions */ + GAnalystClient *client; /* Cible des interactions */ gint active; /* Indice du serveur retenu */ GLoadedBinary *binary; /* Binaire en cours d'étude */ GtkTreeStore *store; /* Modèle de gestion */ /* Déconnexion de l'ancien */ - client = G_HUB_CLIENT(g_object_get_data(G_OBJECT(builder), "client")); + client = G_ANALYST_CLIENT(g_object_get_data(G_OBJECT(builder), "client")); if (client != NULL) g_signal_handlers_disconnect_by_func(client, on_snapshots_updated, builder); @@ -327,7 +327,7 @@ static bool find_suitable_parent(GtkTreeModel *model, GtkTreeIter *iter, const c * * ******************************************************************************/ -static void on_snapshots_updated(GHubClient *client, GtkBuilder *builder) +static void on_snapshots_updated(GAnalystClient *client, GtkBuilder *builder) { GtkTreeStore *store; /* Modèle de gestion */ snapshot_info_t *info; /* Liste d'instantanés présents*/ @@ -351,7 +351,7 @@ static void on_snapshots_updated(GHubClient *client, GtkBuilder *builder) gtk_tree_store_clear(store); - status = g_hub_client_get_snapshots(client, &info, &count); + status = g_analyst_client_get_snapshots(client, &info, &count); if (!status) { @@ -409,7 +409,7 @@ static void on_snapshots_updated(GHubClient *client, GtkBuilder *builder) /* Ajout de l'instantané courant */ - status = g_hub_client_get_current_snapshot(client, ¤t); + status = g_analyst_client_get_current_snapshot(client, ¤t); if (status) { @@ -692,7 +692,7 @@ static void restore_old_snapshot(GtkToolButton *button, GtkBuilder *builder) const char *raw; /* Identifiant brut */ snapshot_id_t id; /* Identifiant utilisable */ bool status; /* Bilan d'une conversion */ - GHubClient *client; /* Cible des interactions */ + GAnalystClient *client; /* Cible des interactions */ raw = g_object_get_data(G_OBJECT(builder), "selected"); @@ -700,9 +700,9 @@ static void restore_old_snapshot(GtkToolButton *button, GtkBuilder *builder) if (status) { - client = G_HUB_CLIENT(g_object_get_data(G_OBJECT(builder), "client")); + client = G_ANALYST_CLIENT(g_object_get_data(G_OBJECT(builder), "client")); - g_hub_client_restore_snapshot(client, &id); + g_analyst_client_restore_snapshot(client, &id); } @@ -724,11 +724,11 @@ static void restore_old_snapshot(GtkToolButton *button, GtkBuilder *builder) static void create_new_snapshot(GtkToolButton *button, GtkBuilder *builder) { - GHubClient *client; /* Cible des interactions */ + GAnalystClient *client; /* Cible des interactions */ - client = G_HUB_CLIENT(g_object_get_data(G_OBJECT(builder), "client")); + client = G_ANALYST_CLIENT(g_object_get_data(G_OBJECT(builder), "client")); - g_hub_client_create_snapshot(client); + g_analyst_client_create_snapshot(client); } @@ -750,7 +750,7 @@ static void remove_old_snapshot(GtkToolButton *button, GtkBuilder *builder) const char *raw; /* Identifiant brut */ snapshot_id_t id; /* Identifiant utilisable */ bool status; /* Bilan d'une conversion */ - GHubClient *client; /* Cible des interactions */ + GAnalystClient *client; /* Cible des interactions */ raw = g_object_get_data(G_OBJECT(builder), "selected"); @@ -758,9 +758,9 @@ static void remove_old_snapshot(GtkToolButton *button, GtkBuilder *builder) if (status) { - client = G_HUB_CLIENT(g_object_get_data(G_OBJECT(builder), "client")); + client = G_ANALYST_CLIENT(g_object_get_data(G_OBJECT(builder), "client")); - g_hub_client_remove_snapshot(client, &id, false); + g_analyst_client_remove_snapshot(client, &id, false); } @@ -785,7 +785,7 @@ static void delete_old_snapshot(GtkToolButton *button, GtkBuilder *builder) const char *raw; /* Identifiant brut */ snapshot_id_t id; /* Identifiant utilisable */ bool status; /* Bilan d'une conversion */ - GHubClient *client; /* Cible des interactions */ + GAnalystClient *client; /* Cible des interactions */ raw = g_object_get_data(G_OBJECT(builder), "selected"); @@ -793,9 +793,9 @@ static void delete_old_snapshot(GtkToolButton *button, GtkBuilder *builder) if (status) { - client = G_HUB_CLIENT(g_object_get_data(G_OBJECT(builder), "client")); + client = G_ANALYST_CLIENT(g_object_get_data(G_OBJECT(builder), "client")); - g_hub_client_remove_snapshot(client, &id, true); + g_analyst_client_remove_snapshot(client, &id, true); } @@ -820,7 +820,7 @@ static void apply_new_snapshot_info(GtkButton *button, GtkBuilder *builder) const char *raw; /* Identifiant brut */ snapshot_id_t id; /* Identifiant utilisable */ bool status; /* Bilan d'une conversion */ - GHubClient *client; /* Cible des interactions */ + GAnalystClient *client; /* Cible des interactions */ GtkEntry *entry; /* Zone de saisie à actualiser */ raw = g_object_get_data(G_OBJECT(builder), "selected"); @@ -829,13 +829,13 @@ static void apply_new_snapshot_info(GtkButton *button, GtkBuilder *builder) if (status) { - client = G_HUB_CLIENT(g_object_get_data(G_OBJECT(builder), "client")); + client = G_ANALYST_CLIENT(g_object_get_data(G_OBJECT(builder), "client")); entry = GTK_ENTRY(gtk_builder_get_object(builder, "name")); - g_hub_client_set_snapshot_name(client, &id, gtk_entry_get_text(entry)); + g_analyst_client_set_snapshot_name(client, &id, gtk_entry_get_text(entry)); entry = GTK_ENTRY(gtk_builder_get_object(builder, "description")); - g_hub_client_set_snapshot_desc(client, &id, gtk_entry_get_text(entry)); + g_analyst_client_set_snapshot_desc(client, &id, gtk_entry_get_text(entry)); } diff --git a/src/gui/panels/history.c b/src/gui/panels/history.c index 79be0dd..d6ca678 100644 --- a/src/gui/panels/history.c +++ b/src/gui/panels/history.c @@ -676,7 +676,7 @@ static void do_history_undo(GtkButton *button, GHistoryPanel *panel) GtkTreeModel *model; /* Modèle de gestion de données*/ GtkTreeIter iter; /* Pointeur vers la ligne visée*/ GDbItem *item; /* Elément de collection */ - GHubClient *client; /* Connexion vers la base */ + GAnalystClient *client; /* Connexion vers la base */ builder = gtk_built_named_widget_get_builder(GTK_BUILT_NAMED_WIDGET(G_PANEL_ITEM(panel)->widget)); @@ -691,7 +691,7 @@ static void do_history_undo(GtkButton *button, GHistoryPanel *panel) gtk_tree_model_get(model, &iter, HTC_ITEM, &item, -1); client = g_loaded_binary_get_client(panel->binary, true); - g_hub_client_set_last_active(client, g_db_item_get_timestamp(item)); + g_analyst_client_set_last_active(client, g_db_item_get_timestamp(item)); g_object_unref(G_OBJECT(client)); g_object_unref(G_OBJECT(item)); @@ -726,7 +726,7 @@ static void do_history_redo(GtkButton *button, GHistoryPanel *panel) GtkTreeModel *model; /* Modèle de gestion de données*/ GtkTreeIter iter; /* Pointeur vers la ligne visée*/ GDbItem *item; /* Elément de collection */ - GHubClient *client; /* Connexion vers la base */ + GAnalystClient *client; /* Connexion vers la base */ builder = gtk_built_named_widget_get_builder(GTK_BUILT_NAMED_WIDGET(G_PANEL_ITEM(panel)->widget)); @@ -739,7 +739,7 @@ static void do_history_redo(GtkButton *button, GHistoryPanel *panel) gtk_tree_model_get(model, &iter, HTC_ITEM, &item, -1); client = g_loaded_binary_get_client(panel->binary, true); - g_hub_client_set_last_active(client, g_db_item_get_timestamp(item)); + g_analyst_client_set_last_active(client, g_db_item_get_timestamp(item)); g_object_unref(G_OBJECT(client)); g_object_unref(G_OBJECT(item)); diff --git a/tests/analysis/db/conn.py b/tests/analysis/db/conn.py new file mode 100644 index 0000000..39c660a --- /dev/null +++ b/tests/analysis/db/conn.py @@ -0,0 +1,125 @@ + +from chrysacase import ChrysalideTestCase +from pychrysalide.analysis.db import certs +from pychrysalide.analysis.db import AdminClient +from pychrysalide.analysis.db import HubServer +import os +import shutil +import tempfile + + +class TestDbConnection(ChrysalideTestCase): + """TestCase for analysis.db.""" + + @classmethod + def setUpClass(cls): + + super(TestDbConnection, cls).setUpClass() + + cls._tmp_path = tempfile.mkdtemp() + + cls._server_path = '%s/.chrysalide/servers/localhost-9999/' % cls._tmp_path + os.makedirs(cls._server_path) + + cls._server_authorized_path = '%s/authorized/' % cls._server_path + os.makedirs(cls._server_authorized_path) + + cls._client_path = '%s/.chrysalide/clients/' % cls._tmp_path + os.makedirs(cls._client_path) + + cls._client_cert_path = '%s/localhost-9999/' % cls._client_path + os.makedirs(cls._client_cert_path) + + cls.log('Using temporary directory "%s"' % cls._tmp_path) + + + @classmethod + def tearDownClass(cls): + + super(TestDbConnection, cls).tearDownClass() + + os.system('ls -laihR %s' % cls._tmp_path) + + cls.log('Delete directory "%s"' % cls._tmp_path) + + shutil.rmtree(cls._tmp_path) + + + def testServerListening(self): + """List binaries available from server.""" + + + from pychrysalide import core + #core.set_verbosity(0) + + + + identity = { + + 'C': 'FR', + 'CN': 'Test authority' + + } + + ret = certs.build_keys_and_ca(self._server_path, 'ca', 3650 * 24 * 60 * 60, identity) + self.assertTrue(ret) + + identity = { + + 'C': 'FR', + 'CN': 'Test server' + + } + + ret = certs.build_keys_and_request(self._server_path, 'server', identity); + self.assertTrue(ret) + + + ret = certs.sign_cert('%s/server-csr.pem' % self._server_path, '%s/ca-cert.pem' % self._server_path, \ + '%s/ca-key.pem' % self._server_path, '%s/server-cert.pem' % self._server_path, \ + 3650 * 24 * 60 * 60) + self.assertTrue(ret) + + identity = { + + 'C': 'FR', + 'CN': 'Test admin' + + } + + ret = certs.build_keys_and_request(self._client_path, 'client', identity); + self.assertTrue(ret) + + ret = certs.sign_cert('%s/client-csr.pem' % self._client_path, '%s/ca-cert.pem' % self._server_path, \ + '%s/ca-key.pem' % self._server_path, '%s/client-cert.pem' % self._client_cert_path, \ + 3650 * 24 * 60 * 60) + self.assertTrue(ret) + + shutil.copy('%s/ca-cert.pem' % self._server_path, + '%s/ca-cert.pem' % self._client_cert_path) + + shutil.copy('%s/client-cert.pem' % self._client_cert_path, + '%s/client-cert.pem' % self._server_authorized_path) + + + os.environ['XDG_CONFIG_HOME'] = self._tmp_path + os.environ['HOME'] = self._tmp_path + + server = HubServer('localhost', '9999') + + #print(server) + + ret = server.start() + + #print(ret) + + admin = AdminClient() + + #print(admin) + + + ret = admin.start('localhost', '9999') + + #print('FINAL::', ret) + + #print(server) -- cgit v0.11.2-87-g4458