diff options
author | Cyrille Bagard <nocbos@gmail.com> | 2021-08-01 16:46:12 (GMT) |
---|---|---|
committer | Cyrille Bagard <nocbos@gmail.com> | 2021-08-01 16:46:12 (GMT) |
commit | 2ea2e6080eec1b06bbd4607203d34f38b75c80f3 (patch) | |
tree | 75cd34437eef2a055616a4554211c96c4e7f9d41 /src | |
parent | 76227d352c21332d1f19e812e157307bdc6c9cf5 (diff) |
Extend the protocol to store the analyzed binary content.
Diffstat (limited to 'src')
-rw-r--r-- | src/analysis/db/analyst.c | 115 | ||||
-rw-r--r-- | src/analysis/db/analyst.h | 4 | ||||
-rw-r--r-- | src/analysis/db/auth.c | 152 | ||||
-rw-r--r-- | src/analysis/db/auth.h | 5 | ||||
-rw-r--r-- | src/analysis/db/cdb.c | 218 | ||||
-rw-r--r-- | src/analysis/db/protocol.h | 64 |
6 files changed, 534 insertions, 24 deletions
diff --git a/src/analysis/db/analyst.c b/src/analysis/db/analyst.c index dff073a..49585c2 100644 --- a/src/analysis/db/analyst.c +++ b/src/analysis/db/analyst.c @@ -26,9 +26,11 @@ #include <assert.h> #include <poll.h> +#include <string.h> #include "client-int.h" +#include "../storage/storage.h" #include "../../core/logs.h" @@ -38,7 +40,7 @@ struct _GAnalystClient { GHubClient parent; /* A laisser en premier */ - rle_string hash; /* Empreinte du binaire lié */ + char *hash; /* Empreinte du binaire lié */ GList *collections; /* Collections d'un binaire */ bool can_get_updates; /* Réception de maj possibles ?*/ @@ -157,7 +159,7 @@ static void g_analyst_client_class_init(GAnalystClientClass *klass) static void g_analyst_client_init(GAnalystClient *client) { - setup_empty_rle_string(&client->hash); + client->hash = NULL; client->collections = NULL; client->can_get_updates = false; @@ -214,7 +216,8 @@ static void g_analyst_client_finalize(GAnalystClient *client) { size_t i; /* Boucle de parcours */ - unset_rle_string(&client->hash); + if (client->hash != NULL) + free(client->hash); if (client->snapshots != NULL) { @@ -249,7 +252,7 @@ GAnalystClient *g_analyst_client_new(const char *hash, GList *collections) result = g_object_new(G_TYPE_ANALYST_CLIENT, NULL); - init_static_rle_string(&result->hash, hash); + result->hash = strdup(hash); result->collections = collections; return result; @@ -273,8 +276,13 @@ GAnalystClient *g_analyst_client_new(const char *hash, GList *collections) static bool g_analyst_client_complete_hello(GAnalystClient *client, packed_buffer_t *pbuf) { bool result; /* Bilan à retourner */ + rle_string str; /* Chaîne à communiquer */ - result = pack_rle_string(&client->hash, pbuf); + init_static_rle_string(&str, client->hash); + + result = pack_rle_string(&str, pbuf); + + exit_rle_string(&str); return result; @@ -406,11 +414,10 @@ static void *g_analyst_client_update(GAnalystClient *client) error = tmp32; if (error == DBE_NONE) - log_variadic_message(LMT_INFO, _("Archive saved for binary '%s'"), - get_rle_string(&client->hash)); + log_variadic_message(LMT_INFO, _("Archive saved for binary '%s'"), client->hash); else log_variadic_message(LMT_ERROR, _("Failed to save the archive for binary '%s'"), - get_rle_string(&client->hash)); + client->hash); break; @@ -635,6 +642,98 @@ static bool g_analyst_client_update_current_snapshot(GAnalystClient *client, pac /****************************************************************************** * * +* Paramètres : client = client pour les accès distants à manipuler. * +* content = contenu binaire à envoyer. * +* * +* Description : Envoie un contenu binaire pour conservation côté serveur. * +* * +* Retour : true si la commande a bien été envoyée, false sinon. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_analyst_client_send_content(GAnalystClient *client, GBinContent *content) +{ + bool result; /* Bilan partiel à remonter */ + const gchar *hash; /* Empreinte du contenu fourni */ + packed_buffer_t cnt_pbuf; /* Tampon de stockage */ + GObjectStorage *storage; /* Gestionnaire de stockage */ + off64_t pos; /* Emplacement du binaire */ + SSL *tls_fd; /* Canal de communication SSL */ + packed_buffer_t out_pbuf; /* Tampon d'émission */ + + result = false; + + /* Validation de la conformité du contenu */ + + hash = g_binary_content_get_checksum(content); + + if (strcmp(hash, client->hash) != 0) + { + log_variadic_message(LMT_ERROR, _("Provided ontent does not match client content (hash: '%s')"), + client->hash); + goto exit; + } + + /* Conversion en format de stockage */ + + init_packed_buffer(&cnt_pbuf); + + storage = g_object_storage_new(client->hash); + + result = g_object_storage_store_object(storage, "contents", G_SERIALIZABLE_OBJECT(content), &pos); + if (!result) goto exit_with_failure; + + result = pack_uleb128((uleb128_t []){ pos }, &cnt_pbuf); + if (!result) goto exit_with_failure; + + result = g_object_storage_store(storage, &cnt_pbuf); + if (!result) goto exit_with_failure; + + /* Transmission */ + + tls_fd = g_hub_client_get_ssl_fd(G_HUB_CLIENT(client)); + + if (tls_fd == NULL) + result = false; + + else + { + init_packed_buffer(&out_pbuf); + + result = extend_packed_buffer(&out_pbuf, (uint32_t []) { DBC_SET_CONTENT }, sizeof(uint32_t), true); + + if (result) + result = pack_uleb128((uleb128_t []){ get_packed_buffer_payload_length(&cnt_pbuf) }, &out_pbuf); + + if (result) + result = include_packed_buffer(&out_pbuf, &cnt_pbuf); + + if (result) + result = ssl_send_packed_buffer(&out_pbuf, tls_fd); + + g_hub_client_put_ssl_fd(G_HUB_CLIENT(client), tls_fd); + + exit_packed_buffer(&out_pbuf); + + } + + exit_with_failure: + + g_object_unref(G_OBJECT(storage)); + + exit_packed_buffer(&cnt_pbuf); + + exit: + + return result; + +} + + +/****************************************************************************** +* * * Paramètres : client = client pour les accès distants à manipuler. * * * * Description : Effectue une demande de sauvegarde de l'état courant. * diff --git a/src/analysis/db/analyst.h b/src/analysis/db/analyst.h index d9e90c6..7b11f53 100644 --- a/src/analysis/db/analyst.h +++ b/src/analysis/db/analyst.h @@ -33,6 +33,7 @@ #include "client.h" #include "collection.h" #include "misc/snapshot.h" +#include "../content.h" @@ -57,6 +58,9 @@ GType g_analyst_client_get_type(void); /* Prépare un client pour une connexion à une BD. */ GAnalystClient *g_analyst_client_new(const char *, GList *); +/* Envoie un contenu binaire pour conservation côté serveur. */ +bool g_analyst_client_send_content(GAnalystClient *, GBinContent *); + /* Effectue une demande de sauvegarde de l'état courant. */ bool g_analyst_client_save(GAnalystClient *); diff --git a/src/analysis/db/auth.c b/src/analysis/db/auth.c index 5e62f58..5869794 100644 --- a/src/analysis/db/auth.c +++ b/src/analysis/db/auth.c @@ -38,6 +38,7 @@ #include "../../common/io.h" #include "../../common/pathname.h" #include "../../common/xdg.h" +#include "../../common/xml.h" #include "../../core/logs.h" @@ -48,6 +49,14 @@ static char *get_cert_storage_directory(const char *, const char *, const char * /* Calcule l'empreinte d'un fichier de demande de signature. */ static char *compute_csr_fingerprint(const char *); +/* Renvoie un accès à la configuration XML des privilèges. */ +static bool open_server_priv_config(const char *, const char *, xmlDocPtr *, xmlXPathContextPtr *); + +/* Assure la présence d'au moins un administrateur. */ +static bool ensure_one_admin_is_registered(const char *, const char *, const char *); + +/* Enregistre et clôture la configuration XML des privilèges. */ +static bool close_server_priv_config(const char *, const char *, xmlDocPtr, xmlXPathContextPtr); /****************************************************************************** @@ -458,6 +467,8 @@ bool add_client_to_server(const char *host, const char *port, unsigned long vali char *cakey; /* Clef de cette autorité */ char *storage; /* Répertoire de stockage */ char *dest; /* Destination d'une copie */ + x509_entries entries; /* Identitié du client */ + char *id; /* Identifiant associé */ result = false; @@ -517,6 +528,22 @@ bool add_client_to_server(const char *host, const char *port, unsigned long vali } + if (result) + { + result = load_identity_from_cert(hash, &entries); + + if (result) + { + id = translate_x509_entries(&entries); + + result = ensure_one_admin_is_registered(host, port, id); + + free_x509_entries(&entries); + + } + + } + free(storage); } @@ -539,9 +566,132 @@ bool add_client_to_server(const char *host, const char *port, unsigned long vali /****************************************************************************** * * +* Paramètres : host = désignation du serveur à contacter. * +* port = port d'écoute correspondant. * +* xdoc = document XML prêt à emploi. [OUT] * +* context = contexte de recherche XPath. [OUT] * +* * +* Description : Renvoie un accès à la configuration XML des privilèges. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool open_server_priv_config(const char *host, const char *port, xmlDocPtr *xdoc, xmlXPathContextPtr *context) +{ + bool result; /* Bilan à retourner */ + char *filename; /* Chemin d'accès à la config. */ + int ret; /* Test de présence de fichier */ + + filename = get_db_working_directory("servers", host, port, NULL); + filename = stradd(filename, "privs.xml"); + + ret = access(filename, F_OK); + + if (ret == 0) + result = open_xml_file(filename, xdoc, context); + + else + { + result = create_new_xml_file(xdoc, context); + + if (result) + result = (ensure_node_exist(*xdoc, *context, "/ServerPrivLevels") != NULL); + + } + + free(filename); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : host = désignation du serveur à contacter. * +* port = port d'écoute correspondant. * +* id = identification d'un utilisateur. * +* * +* Description : Assure la présence d'au moins un administrateur. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool ensure_one_admin_is_registered(const char *host, const char *port, const char *id) +{ + bool result; /* Bilan à retourner */ + xmlDocPtr xdoc; /* Document XML de configurat° */ + xmlXPathContextPtr context; /* Contexte de recherche XPath */ + xmlXPathObjectPtr xobject; /* Cible d'une recherche */ + size_t count; /* Nombre de contenus premiers */ + + result = open_server_priv_config(host, port, &xdoc, &context); + if (!result) goto exit; + + xobject = get_node_xpath_object(context, "/ServerPrivLevels/Administrators/User"); + + count = XPATH_OBJ_NODES_COUNT(xobject); + + if (count == 0) + result = add_content_to_node(xdoc, context, "/ServerPrivLevels/Administrators/User", id); + + if(xobject != NULL) + xmlXPathFreeObject(xobject); + + result = close_server_priv_config(host, port, xdoc, context); + + exit: + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : host = désignation du serveur à contacter. * +* port = port d'écoute correspondant. * +* xdoc = document XML prêt à emploi. * +* context = contexte de recherche XPath. * +* * +* Description : Enregistre et clôture la configuration XML des privilèges. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool close_server_priv_config(const char *host, const char *port, xmlDocPtr xdoc, xmlXPathContextPtr context) +{ + bool result; /* Bilan à retourner */ + char *filename; /* Chemin d'accès à la config. */ + + filename = get_db_working_directory("servers", host, port, NULL); + filename = stradd(filename, "privs.xml"); + + result = save_xml_file(xdoc, filename); + + close_xml_file(xdoc, context); + + free(filename); + + return result; + +} + + +/****************************************************************************** +* * * Paramètres : - * * * -* Description : Assure la présence d'unenvironnement pour serveur interne. * +* Description : Assure la présence d'un environnement pour serveur interne. * * * * Retour : Bilan de l'opération. * * * diff --git a/src/analysis/db/auth.h b/src/analysis/db/auth.h index 4464244..70b8a9e 100644 --- a/src/analysis/db/auth.h +++ b/src/analysis/db/auth.h @@ -30,7 +30,7 @@ #include "certs.h" - +#include "protocol.h" /* Met en place un canal UNIX pour un serveur interne. */ @@ -51,6 +51,9 @@ bool setup_server_identity(const char *, const char *, unsigned long, x509_entri /* Ajoute un certificat dans les utilisateurs d'un serveur. */ bool add_client_to_server(const char *, const char *, unsigned long, const char *, const char *); + + + /* Assure la présence d'unenvironnement pour serveur interne. */ bool ensure_internal_connections_setup(void); diff --git a/src/analysis/db/cdb.c b/src/analysis/db/cdb.c index 4da55fd..e4179d3 100644 --- a/src/analysis/db/cdb.c +++ b/src/analysis/db/cdb.c @@ -26,6 +26,7 @@ #include <assert.h> #include <errno.h> +#include <fcntl.h> #include <malloc.h> #include <poll.h> #include <pthread.h> @@ -45,9 +46,12 @@ #include "collection.h" #include "protocol.h" #include "snapshot.h" +#include "../content.h" +#include "../storage/storage.h" #include "../../common/compression.h" #include "../../common/cpp.h" #include "../../common/extstr.h" +#include "../../common/io.h" #include "../../common/pathname.h" #include "../../common/xml.h" #include "../../core/collections.h" @@ -79,6 +83,7 @@ struct _GCdbArchive char *filename; /* Chemin d'accès à l'archive */ char *tmpdir; /* Répertoire de travail */ + char *cnt_file; /* Fichier de contenu binaire */ char *xml_desc; /* Fichier de description */ GList *collections; /* Ensemble de modifications */ @@ -163,6 +168,17 @@ static bool g_cdb_archive_send_snapshot_change(GCdbArchive *, packed_buffer_t *) +/* ------------------------- PRISES EN COMPTE DES COMMANDES ------------------------- */ + + +/* Prépare la réponse à envoyer à un client connecté. */ +static bool setup_server_answer(DBCommand, DBError, packed_buffer_t *); + +/* Enregistre le contenu binaire lié à une analyse. */ +static bool g_cdb_archive_set_content(GCdbArchive *, packed_buffer_t *, packed_buffer_t *); + + + /* ---------------------------------------------------------------------------------- */ /* COEUR DE LA GESTION D'ARCHIVES */ /* ---------------------------------------------------------------------------------- */ @@ -222,6 +238,8 @@ static void g_cdb_archive_init(GCdbArchive *archive) archive->filename = NULL; archive->tmpdir = NULL; + archive->cnt_file = NULL; + archive->xml_desc = NULL; archive->collections = create_collections_list(); @@ -285,15 +303,18 @@ static void g_cdb_archive_finalize(GCdbArchive *archive) #endif } + if (archive->cnt_file != NULL) + free(archive->cnt_file); + if (archive->xml_desc != NULL) free(archive->xml_desc); - if (archive->filename != NULL) - free(archive->filename); - if (archive->tmpdir != NULL) free(archive->tmpdir); + if (archive->filename != NULL) + free(archive->filename); + exit_rle_string(&archive->hash); G_OBJECT_CLASS(g_cdb_archive_parent_class)->finalize(G_OBJECT(archive)); @@ -932,6 +953,19 @@ static void *g_cdb_archive_process(GCdbArchive *archive) switch (command) { + case DBC_SET_CONTENT: + + status = g_cdb_archive_set_content(archive, &in_pbuf, &out_pbuf); + if (!status) goto gcap_bad_reply; + + status = ssl_send_packed_buffer(&out_pbuf, archive->clients[i].tls_fd); + if (!status) goto gcap_bad_reply; + + exit_packed_buffer(&out_pbuf); + break; + + + case DBC_SAVE: error = g_cdb_archive_write(archive); @@ -1451,3 +1485,181 @@ static bool g_cdb_archive_send_snapshot_change(GCdbArchive *archive, packed_buff return result; } + + + +/* ---------------------------------------------------------------------------------- */ +/* PRISES EN COMPTE DES COMMANDES */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : cmd = commande à l'origine d'un traitement. * +* error = bilan de traitement à communiquer. * +* out_pbuf = paquet à consituer pour un retour au client. [OUT]* +* * +* Description : Prépare la réponse à envoyer à un client connecté. * +* * +* Retour : Indication pour le maintien de la communication. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool setup_server_answer(DBCommand cmd, DBError error, packed_buffer_t *out_pbuf) +{ + bool result; /* Bilan à retourner */ + + init_packed_buffer(out_pbuf); + + result = extend_packed_buffer(out_pbuf, (uint32_t []) { cmd }, sizeof(uint32_t), true); + + if (result) + result = extend_packed_buffer(out_pbuf, (uint32_t []) { error }, sizeof(uint32_t), true); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : archive = archive à connecter avec un utilisateur. * +* in_pbuf = paquet à consulter. * +* out_pbuf = paquet à consituer pour un retour au client. [OUT]* +* * +* Description : Enregistre le contenu binaire lié à une analyse. * +* * +* Retour : Indication pour le maintien de la communication. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool g_cdb_archive_set_content(GCdbArchive *archive, packed_buffer_t *in_pbuf, packed_buffer_t *out_pbuf) +{ + bool result; /* Bilan à retourner */ + DBError error; /* Bilan d'une opération */ + uleb128_t data_length; /* Taille du contenu stocké */ + void *data; /* Données du stockage */ + packed_buffer_t test_pbuf; /* Copie des données pour test */ + uleb128_t pos; /* Position du contenu */ + GObjectStorage *storage; /* Gestionnaire de stockage */ + GSerializableObject *content; /* Contenu restitué */ + const gchar *hash; /* Empreinte de ce contenu */ + int ret; /* Retour d'un appel */ + int fd; /* Flux ouvert en écriture */ + bool status; /* Bilan d'une écriture */ + + result = true; + error = DBE_NONE; + + /* Récupération de la charge utile */ + + result = unpack_uleb128(&data_length, in_pbuf); + if (!result) goto exit; + + data = malloc(data_length); + + result = extract_packed_buffer(in_pbuf, data, data_length, false); + if (!result) goto free_and_exit; + + /* Validation de l'empreinte du contenu */ + + init_packed_buffer(&test_pbuf); + + result = extend_packed_buffer(&test_pbuf, data, data_length, false); + if (!result) goto check_failure; + + rewind_packed_buffer(&test_pbuf); + + result = unpack_uleb128(&pos, &test_pbuf); + if (!result) goto check_failure; + + storage = g_object_storage_load(&test_pbuf); + if (storage == NULL) + { + result = false; + goto check_failure; + } + + content = g_object_storage_load_object(storage, "contents", pos); + if (!G_IS_BIN_CONTENT(content)) + { + result = false; + goto storage_check_failure; + } + + hash = g_binary_content_get_checksum(G_BIN_CONTENT(content)); + + if (strcmp(hash, get_rle_string(&archive->hash)) != 0) + error = DBE_WRONG_HASH; + + g_object_unref(G_OBJECT(content)); + + storage_check_failure: + + g_object_unref(G_OBJECT(storage)); + + check_failure: + + exit_packed_buffer(&test_pbuf); + + if (!result) goto free_and_exit; + + /* Enregistrement sur disque */ + + if (error == DBE_NONE) + { + if (archive->cnt_file != NULL) + free(archive->cnt_file); + + ret = asprintf(&archive->cnt_file, "%s" G_DIR_SEPARATOR_S "%s_storedcontent.bin", + archive->tmpdir, get_rle_string(&archive->hash)); + if (ret == -1) + { + error = DBE_SYS_ERROR; + goto save_error; + } + + fd = open(archive->cnt_file, O_WRONLY | O_CREAT, 0600); + if (fd == -1) + { + error = DBE_SYS_ERROR; + goto save_error; + } + + status = safe_write(fd, data, data_length); + + if (!status) + { + unlink(archive->cnt_file); + free(archive->cnt_file); + archive->cnt_file = NULL; + + error = DBE_SYS_ERROR; + + } + + close(fd); + + save_error: + + ; + + } + + /* Formulation de la réponse */ + + result = setup_server_answer(DBC_SET_CONTENT, error, out_pbuf); + + free_and_exit: + + free(data); + + exit: + + return result; + +} diff --git a/src/analysis/db/protocol.h b/src/analysis/db/protocol.h index 4bef28e..17263c8 100644 --- a/src/analysis/db/protocol.h +++ b/src/analysis/db/protocol.h @@ -47,15 +47,6 @@ -/* Comportement vis à vis des éléments */ -typedef enum _DBStorage -{ - DBS_ALL_LOCAL = 0x00, /* Enregistrements locaux */ - DBS_ALL_REMOTE = 0x01, /* Enregistrements distants */ - - DBS_MAX = 0x01 - -} DBStorage; @@ -71,17 +62,36 @@ typedef enum _ClientRole } ClientRole; +/* Niveaux de privilèges */ +typedef enum _ServerPrivLevels +{ + SPV_UNDEFINED = 0, /* Rôle non défini */ + SPV_ADMINISTRATOR = 1, /* Pleins pouvoirs */ + SPV_MANAGER = 2, /* Gestionnaire de comptes */ + SPV_CREATOR = 3, /* Gestionnaire d'analyses */ + SPV_ANALYST = 4, /* Analyste de binaires */ +} ServerPrivLevels; -/** - * Une fois la connexion établie, les paquets ont tous la forme suivante : + +/* Eléments de base nécessaires */ +typedef enum _RequiredBasics +{ + RBS_NONE = 0x0, /* (Plus) rien n'est requis */ + RBS_CONTENT = 0x1, /* Contenu binaire à analyser */ + RBS_LOADED = 0x2, /* Contenu binaire analysé */ + +} RequiredBasics; + +/** + * Une fois la connexion établie, les paquets ont tous la forme suivante : * * [ type de collection visée ; cf. DBFeatures ] * [ action à mener ; cf. DBAction ] @@ -166,6 +176,37 @@ typedef enum _DBCommand /* ------------------------ Commandes pour analyste ------------------------ */ /** + * Gestion de la commande 'DBC_SET_CONTENT'. + * + * Le client connecté envoie un paquet de la forme suivante : + * + * [ Ordre de sauvegarde : DBC_SET_CONTENT ] + * [ Quantité des données suivantes ] + * [ Position du contenu + données de stockage ] + * + * Le serveur s'exécute et renvoie un bilan : + * + * [ Ordre de sauvegarde : DBC_SET_CONTENT ] + * [ Statut d'exécution ; cf. DBError ] + * + */ + + DBC_SET_CONTENT, + + + + + + + + + + + + + + + /** * Gestion de la commande 'DBC_SAVE'. * * Le client connecté envoie un paquet de la forme suivante : @@ -377,6 +418,7 @@ typedef enum _DBError DBE_XML_VERSION_ERROR, /* Vieille archive présente */ DBE_DB_LOADING_ERROR, /* Erreur pendant le chargement*/ + DBE_WRONG_HASH, /* Empreinte inattendue */ DBE_XML_ERROR, /* Erreur lors d'une définition*/ DBE_SNAPSHOT_NOT_FOUND, /* Instantané non trouvé */ DBE_SNAPSHOT_RESTORE_FAILURE, /* Echec d'une restauration */ |