feat: add asynchronous evidence integrity task
This commit is contained in:
parent
a4765e8de2
commit
5ad959315a
10 changed files with 4302 additions and 3 deletions
34
Makefile
34
Makefile
|
|
@ -37,6 +37,10 @@ EVIDENCE_IMPORT_DIALOG_TEST_CFLAGS := \
|
|||
EVIDENCE_IMPORT_DIALOG_TEST_LDFLAGS := \
|
||||
$(shell $(PKG_CONFIG) --libs gtk4)
|
||||
|
||||
EVIDENCE_INTEGRITY_VERIFIER_TEST_CFLAGS := \
|
||||
$(TEST_CFLAGS) \
|
||||
-Wpedantic
|
||||
|
||||
SRC := $(shell find src -name "*.c")
|
||||
|
||||
OBJ := $(SRC:.c=.o)
|
||||
|
|
@ -64,6 +68,7 @@ TEST_TOOL_TASK := tests/test_tool_task
|
|||
TEST_TOOL_CATALOG := tests/test_tool_catalog
|
||||
TEST_TOOL_INITIALIZER := tests/test_tool_initializer
|
||||
TEST_FILE_HASH := tests/test_file_hash
|
||||
TEST_EVIDENCE_INTEGRITY_VERIFIER := tests/test_evidence_integrity_verifier
|
||||
TEST_EVIDENCE_COPY := tests/test_evidence_copy
|
||||
TEST_EVIDENCE_IMPORTER := tests/test_evidence_importer
|
||||
TEST_EVIDENCE_IMPORT_TASK := tests/test_evidence_import_task
|
||||
|
|
@ -74,6 +79,7 @@ TEST_EVIDENCE_LIST_ITEM := tests/test_evidence_list_item
|
|||
TEST_EVIDENCE_LIST_MODEL := tests/test_evidence_list_model
|
||||
TEST_EVIDENCE_CATEGORY_ITEM := tests/test_evidence_category_item
|
||||
TEST_EVIDENCE_CATEGORY_MODEL := tests/test_evidence_category_model
|
||||
TEST_EVIDENCE_INTEGRITY_TASK := tests/test_evidence_integrity_task
|
||||
|
||||
all: $(TARGET)
|
||||
|
||||
|
|
@ -254,6 +260,13 @@ $(TEST_FILE_HASH): \
|
|||
-DFILE_HASH_ENABLE_TEST_HOOKS \
|
||||
$^ -o $@ $(TEST_LDFLAGS)
|
||||
|
||||
$(TEST_EVIDENCE_INTEGRITY_VERIFIER): \
|
||||
tests/test_evidence_integrity_verifier.c \
|
||||
src/core/evidence_integrity_verifier.c \
|
||||
src/core/file_hash.c
|
||||
$(CC) $(EVIDENCE_INTEGRITY_VERIFIER_TEST_CFLAGS) \
|
||||
$^ -o $@ $(TEST_LDFLAGS)
|
||||
|
||||
$(TEST_EVIDENCE_COPY): \
|
||||
tests/test_evidence_copy.c \
|
||||
src/core/evidence_copy.c \
|
||||
|
|
@ -336,6 +349,17 @@ $(TEST_EVIDENCE_CATEGORY_MODEL): \
|
|||
src/models/evidence_record.c
|
||||
$(CC) $(TEST_CFLAGS) $^ -o $@ $(TEST_LDFLAGS)
|
||||
|
||||
$(TEST_EVIDENCE_INTEGRITY_TASK): \
|
||||
tests/test_evidence_integrity_task.c \
|
||||
src/core/evidence_integrity_task.c \
|
||||
src/core/evidence_integrity_verifier.c \
|
||||
src/core/file_hash.c \
|
||||
src/core/background_task.c \
|
||||
src/core/task_manager.c
|
||||
$(CC) $(TEST_CFLAGS) \
|
||||
-DFILE_HASH_ENABLE_TEST_HOOKS \
|
||||
$^ -o $@ $(TEST_LDFLAGS)
|
||||
|
||||
test: \
|
||||
$(TEST_NODE) \
|
||||
$(TEST_TREE_MODEL) \
|
||||
|
|
@ -364,10 +388,12 @@ test: \
|
|||
$(TEST_TOOL_CATALOG) \
|
||||
$(TEST_TOOL_INITIALIZER) \
|
||||
$(TEST_FILE_HASH) \
|
||||
$(TEST_EVIDENCE_INTEGRITY_VERIFIER) \
|
||||
$(TEST_EVIDENCE_COPY) \
|
||||
$(TEST_EVIDENCE_IMPORTER) \
|
||||
$(TEST_EVIDENCE_IMPORT_TASK) \
|
||||
$(TEST_EVIDENCE_IMPORT_DIALOG)
|
||||
$(TEST_EVIDENCE_IMPORT_DIALOG) \
|
||||
$(TEST_EVIDENCE_INTEGRITY_TASK)
|
||||
@echo "Exécution des tests..."
|
||||
@./$(TEST_NODE)
|
||||
@./$(TEST_TREE_MODEL)
|
||||
|
|
@ -396,10 +422,12 @@ test: \
|
|||
@$(TEST_TOOL_CATALOG)
|
||||
@$(TEST_TOOL_INITIALIZER)
|
||||
@$(TEST_FILE_HASH)
|
||||
@$(TEST_EVIDENCE_INTEGRITY_VERIFIER)
|
||||
@$(TEST_EVIDENCE_COPY)
|
||||
@$(TEST_EVIDENCE_IMPORTER)
|
||||
@$(TEST_EVIDENCE_IMPORT_TASK)
|
||||
@$(TEST_EVIDENCE_IMPORT_DIALOG)
|
||||
@$(TEST_EVIDENCE_INTEGRITY_TASK)
|
||||
@echo "Tous les tests sont valides."
|
||||
|
||||
%.o: %.c
|
||||
|
|
@ -437,10 +465,12 @@ clean:
|
|||
$(TEST_TOOL_CATALOG) \
|
||||
$(TEST_TOOL_INITIALIZER) \
|
||||
$(TEST_FILE_HASH) \
|
||||
$(TEST_EVIDENCE_INTEGRITY_VERIFIER) \
|
||||
$(TEST_EVIDENCE_COPY) \
|
||||
$(TEST_EVIDENCE_IMPORTER) \
|
||||
$(TEST_EVIDENCE_IMPORT_TASK) \
|
||||
$(TEST_EVIDENCE_IMPORT_DIALOG)
|
||||
$(TEST_EVIDENCE_IMPORT_DIALOG) \
|
||||
$(TEST_EVIDENCE_INTEGRITY_TASK)
|
||||
|
||||
|
||||
.PHONY: clean run test
|
||||
|
|
|
|||
80
include/core/evidence_integrity_task.h
Normal file
80
include/core/evidence_integrity_task.h
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
/******************************************************************************
|
||||
* @file evidence_integrity_task.h
|
||||
* @brief Tâche asynchrone de vérification d'intégrité d'une preuve.
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef LABFY_INVESTIGATION_EVIDENCE_INTEGRITY_TASK_H
|
||||
#define LABFY_INVESTIGATION_EVIDENCE_INTEGRITY_TASK_H
|
||||
|
||||
#include "core/background_task.h"
|
||||
#include "core/task_manager.h"
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
/**
|
||||
* @brief Codes d'erreur propres à la tâche de vérification.
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
EVIDENCE_INTEGRITY_TASK_ERROR_INVALID_ARGUMENT,
|
||||
EVIDENCE_INTEGRITY_TASK_ERROR_VERIFY
|
||||
} EvidenceIntegrityTaskError;
|
||||
|
||||
/**
|
||||
* @brief Domaine d'erreur du module.
|
||||
*/
|
||||
#define EVIDENCE_INTEGRITY_TASK_ERROR \
|
||||
evidence_integrity_task_error_quark()
|
||||
|
||||
/**
|
||||
* @brief Paramètres nécessaires à une vérification d'intégrité.
|
||||
*
|
||||
* Toutes les chaînes sont copiées avant le démarrage du worker.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
const char *investigation_root_path;
|
||||
const char *relative_path;
|
||||
const char *expected_sha256;
|
||||
} EvidenceIntegrityTaskRequest;
|
||||
|
||||
/**
|
||||
* @brief Retourne le domaine d'erreur du module.
|
||||
*/
|
||||
GQuark evidence_integrity_task_error_quark(void);
|
||||
|
||||
/**
|
||||
* @brief Crée, enregistre et démarre une tâche de vérification.
|
||||
*
|
||||
* Le résultat final est un EvidenceIntegrityVerificationResult emprunté
|
||||
* à la BackgroundTask. Il reste valide tant que la tâche existe.
|
||||
*
|
||||
* En cas de succès :
|
||||
*
|
||||
* - TaskManager conserve une référence ;
|
||||
* - le worker conserve une référence pendant son exécution ;
|
||||
* - l'appelant reçoit sa référence initiale et doit la libérer avec
|
||||
* background_task_unref().
|
||||
*
|
||||
* completion_data est transféré à BackgroundTask uniquement si le
|
||||
* démarrage réussit. En cas d'échec, l'appelant en reste propriétaire.
|
||||
*
|
||||
* @param task_manager Gestionnaire recevant la tâche.
|
||||
* @param request Paramètres de la vérification.
|
||||
* @param completion_callback Callback final facultatif.
|
||||
* @param completion_data Données du callback final.
|
||||
* @param completion_data_destroy Destructeur de completion_data.
|
||||
* @param error Emplacement facultatif recevant une erreur.
|
||||
*
|
||||
* @return Nouvelle tâche démarrée, ou NULL.
|
||||
*/
|
||||
BackgroundTask *evidence_integrity_task_start(
|
||||
TaskManager *task_manager,
|
||||
const EvidenceIntegrityTaskRequest *request,
|
||||
BackgroundTaskCompletionCallback completion_callback,
|
||||
gpointer completion_data,
|
||||
GDestroyNotify completion_data_destroy,
|
||||
GError **error
|
||||
);
|
||||
|
||||
#endif
|
||||
115
include/core/evidence_integrity_verifier.h
Normal file
115
include/core/evidence_integrity_verifier.h
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
/******************************************************************************
|
||||
* @file evidence_integrity_verifier.h
|
||||
* @brief Vérification de l'intégrité d'une preuve numérique.
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef LABFY_INVESTIGATION_EVIDENCE_INTEGRITY_VERIFIER_H
|
||||
#define LABFY_INVESTIGATION_EVIDENCE_INTEGRITY_VERIFIER_H
|
||||
|
||||
#include "models/evidence_record.h"
|
||||
|
||||
#include <gio/gio.h>
|
||||
#include <glib.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
/**
|
||||
* @brief Erreurs techniques du vérificateur.
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
EVIDENCE_INTEGRITY_VERIFIER_ERROR_INVALID_ARGUMENT,
|
||||
EVIDENCE_INTEGRITY_VERIFIER_ERROR_ROOT,
|
||||
EVIDENCE_INTEGRITY_VERIFIER_ERROR_CANCELLED,
|
||||
EVIDENCE_INTEGRITY_VERIFIER_ERROR_MEMORY
|
||||
} EvidenceIntegrityVerifierError;
|
||||
|
||||
#define EVIDENCE_INTEGRITY_VERIFIER_ERROR \
|
||||
evidence_integrity_verifier_error_quark()
|
||||
|
||||
GQuark evidence_integrity_verifier_error_quark(void);
|
||||
|
||||
/**
|
||||
* @brief Résultat opaque d'une vérification.
|
||||
*/
|
||||
typedef struct EvidenceIntegrityVerificationResult
|
||||
EvidenceIntegrityVerificationResult;
|
||||
|
||||
/**
|
||||
* @brief Vérifie une preuve à partir de son chemin relatif.
|
||||
*
|
||||
* Le chemin est résolu sous investigation_root_path.
|
||||
* Les chemins absolus, les composants "." et ".." et les liens
|
||||
* symboliques sont refusés.
|
||||
*
|
||||
* Les résultats MISSING et ERROR sont des résultats métier normaux :
|
||||
* la fonction retourne alors un objet résultat sans remplir GError.
|
||||
*
|
||||
* Une erreur technique d'argument, de racine, de mémoire ou une
|
||||
* annulation retourne NULL.
|
||||
*
|
||||
* @param investigation_root_path Racine de l'enquête.
|
||||
* @param relative_path Chemin relatif enregistré dans SQLite.
|
||||
* @param expected_sha256 Empreinte enregistrée.
|
||||
* @param cancellable Objet d'annulation facultatif.
|
||||
* @param error Adresse recevant une erreur technique.
|
||||
*
|
||||
* @return Nouveau résultat possédé par l'appelant, ou NULL.
|
||||
*/
|
||||
EvidenceIntegrityVerificationResult *
|
||||
evidence_integrity_verifier_verify(
|
||||
const char *investigation_root_path,
|
||||
const char *relative_path,
|
||||
const char *expected_sha256,
|
||||
GCancellable *cancellable,
|
||||
GError **error
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Retourne le statut calculé.
|
||||
*/
|
||||
EvidenceIntegrityStatus
|
||||
evidence_integrity_verification_result_get_status(
|
||||
const EvidenceIntegrityVerificationResult *result
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Retourne l'empreinte recalculée.
|
||||
*
|
||||
* La chaîne est empruntée. Elle vaut NULL lorsque le fichier
|
||||
* n'a pas pu être lu.
|
||||
*/
|
||||
const char *
|
||||
evidence_integrity_verification_result_get_computed_sha256(
|
||||
const EvidenceIntegrityVerificationResult *result
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Retourne la taille effectivement lue.
|
||||
*/
|
||||
guint64 evidence_integrity_verification_result_get_size_bytes(
|
||||
const EvidenceIntegrityVerificationResult *result
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Retourne un diagnostic technique facultatif.
|
||||
*
|
||||
* La chaîne est empruntée.
|
||||
*/
|
||||
const char *
|
||||
evidence_integrity_verification_result_get_diagnostic(
|
||||
const EvidenceIntegrityVerificationResult *result
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Libère un résultat.
|
||||
*
|
||||
* Cette fonction accepte NULL.
|
||||
*/
|
||||
void evidence_integrity_verification_result_free(
|
||||
EvidenceIntegrityVerificationResult *result
|
||||
);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif
|
||||
|
|
@ -17,6 +17,7 @@
|
|||
typedef enum
|
||||
{
|
||||
EVIDENCE_DAO_ERROR_INVALID_ARGUMENT,
|
||||
EVIDENCE_DAO_ERROR_NOT_FOUND,
|
||||
EVIDENCE_DAO_ERROR_MEMORY,
|
||||
EVIDENCE_DAO_ERROR_PREPARE,
|
||||
EVIDENCE_DAO_ERROR_BIND,
|
||||
|
|
@ -124,6 +125,25 @@ GPtrArray *evidence_dao_list_all(
|
|||
GError **error
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Met à jour le statut d'intégrité d'une preuve.
|
||||
*
|
||||
* Seule la colonne du statut d'intégrité est modifiée.
|
||||
*
|
||||
* @param evidence_dao DAO valide.
|
||||
* @param identifier Identifiant UUID de la preuve.
|
||||
* @param integrity_status Nouveau statut d'intégrité.
|
||||
* @param error Adresse recevant une éventuelle erreur.
|
||||
*
|
||||
* @return TRUE si la mise à jour réussit, sinon FALSE.
|
||||
*/
|
||||
gboolean evidence_dao_update_integrity_status(
|
||||
EvidenceDao *evidence_dao,
|
||||
const char *identifier,
|
||||
EvidenceIntegrityStatus integrity_status,
|
||||
GError **error
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Compte les preuves persistées.
|
||||
*
|
||||
|
|
|
|||
522
src/core/evidence_integrity_task.c
Normal file
522
src/core/evidence_integrity_task.c
Normal file
|
|
@ -0,0 +1,522 @@
|
|||
/******************************************************************************
|
||||
* @file evidence_integrity_task.c
|
||||
* @brief Exécution asynchrone de la vérification d'intégrité.
|
||||
******************************************************************************/
|
||||
|
||||
#include "core/evidence_integrity_task.h"
|
||||
|
||||
#include "core/evidence_integrity_verifier.h"
|
||||
|
||||
#include <gio/gio.h>
|
||||
#include <glib.h>
|
||||
|
||||
/**
|
||||
* @brief Copie privée des paramètres nécessaires au worker.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
char *investigation_root_path;
|
||||
char *relative_path;
|
||||
char *expected_sha256;
|
||||
} EvidenceIntegrityTaskData;
|
||||
|
||||
/**
|
||||
* @brief Libère les données privées du worker.
|
||||
*/
|
||||
static void evidence_integrity_task_data_free(
|
||||
gpointer user_data
|
||||
)
|
||||
{
|
||||
EvidenceIntegrityTaskData *task_data =
|
||||
user_data;
|
||||
|
||||
if (task_data == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
g_free(
|
||||
task_data->expected_sha256
|
||||
);
|
||||
|
||||
g_free(
|
||||
task_data->relative_path
|
||||
);
|
||||
|
||||
g_free(
|
||||
task_data->investigation_root_path
|
||||
);
|
||||
|
||||
g_free(
|
||||
task_data
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Vérifie les paramètres obligatoires.
|
||||
*/
|
||||
static gboolean evidence_integrity_task_request_is_valid(
|
||||
const EvidenceIntegrityTaskRequest *request
|
||||
)
|
||||
{
|
||||
if (request == NULL)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (request->investigation_root_path == NULL ||
|
||||
request->investigation_root_path[0] == '\0')
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (request->relative_path == NULL ||
|
||||
request->relative_path[0] == '\0')
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (request->expected_sha256 == NULL ||
|
||||
request->expected_sha256[0] == '\0')
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Copie les paramètres avant le lancement du thread.
|
||||
*/
|
||||
static EvidenceIntegrityTaskData *
|
||||
evidence_integrity_task_data_new(
|
||||
const EvidenceIntegrityTaskRequest *request
|
||||
)
|
||||
{
|
||||
EvidenceIntegrityTaskData *task_data =
|
||||
NULL;
|
||||
|
||||
if (!evidence_integrity_task_request_is_valid(
|
||||
request
|
||||
))
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
task_data =
|
||||
g_try_new0(
|
||||
EvidenceIntegrityTaskData,
|
||||
1
|
||||
);
|
||||
|
||||
if (task_data == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
task_data->investigation_root_path =
|
||||
g_strdup(
|
||||
request->investigation_root_path
|
||||
);
|
||||
|
||||
task_data->relative_path =
|
||||
g_strdup(
|
||||
request->relative_path
|
||||
);
|
||||
|
||||
task_data->expected_sha256 =
|
||||
g_strdup(
|
||||
request->expected_sha256
|
||||
);
|
||||
|
||||
return task_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Libère le résultat de vérification d'une tâche.
|
||||
*/
|
||||
static void evidence_integrity_task_result_free(
|
||||
gpointer user_data
|
||||
)
|
||||
{
|
||||
evidence_integrity_verification_result_free(
|
||||
user_data
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Traduit une annulation métier en annulation GIO.
|
||||
*
|
||||
* BackgroundTask reconnaît l'état CANCELLED uniquement avec :
|
||||
*
|
||||
* G_IO_ERROR / G_IO_ERROR_CANCELLED.
|
||||
*/
|
||||
static void evidence_integrity_task_propagate_verify_error(
|
||||
GError **error,
|
||||
GError *verify_error
|
||||
)
|
||||
{
|
||||
if (verify_error != NULL &&
|
||||
g_error_matches(
|
||||
verify_error,
|
||||
EVIDENCE_INTEGRITY_VERIFIER_ERROR,
|
||||
EVIDENCE_INTEGRITY_VERIFIER_ERROR_CANCELLED
|
||||
))
|
||||
{
|
||||
g_clear_error(
|
||||
&verify_error
|
||||
);
|
||||
|
||||
g_set_error_literal(
|
||||
error,
|
||||
G_IO_ERROR,
|
||||
G_IO_ERROR_CANCELLED,
|
||||
"La vérification d'intégrité a été annulée."
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (verify_error != NULL)
|
||||
{
|
||||
g_propagate_error(
|
||||
error,
|
||||
verify_error
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
g_set_error_literal(
|
||||
error,
|
||||
EVIDENCE_INTEGRITY_TASK_ERROR,
|
||||
EVIDENCE_INTEGRITY_TASK_ERROR_VERIFY,
|
||||
"La vérification a échoué sans fournir d'erreur."
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Retourne le message final correspondant au statut métier.
|
||||
*/
|
||||
static const char *evidence_integrity_task_get_status_message(
|
||||
EvidenceIntegrityStatus integrity_status
|
||||
)
|
||||
{
|
||||
switch (integrity_status)
|
||||
{
|
||||
case EVIDENCE_INTEGRITY_STATUS_VALID:
|
||||
return "Intégrité vérifiée : fichier intact";
|
||||
|
||||
case EVIDENCE_INTEGRITY_STATUS_MODIFIED:
|
||||
return "Intégrité compromise : empreinte différente";
|
||||
|
||||
case EVIDENCE_INTEGRITY_STATUS_MISSING:
|
||||
return "Vérification terminée : fichier absent";
|
||||
|
||||
case EVIDENCE_INTEGRITY_STATUS_ERROR:
|
||||
return "Vérification terminée : erreur de lecture";
|
||||
|
||||
case EVIDENCE_INTEGRITY_STATUS_UNKNOWN:
|
||||
default:
|
||||
return "Vérification terminée";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Exécute la vérification dans le thread secondaire.
|
||||
*/
|
||||
static gboolean evidence_integrity_task_worker(
|
||||
BackgroundTask *task,
|
||||
GCancellable *cancellable,
|
||||
gpointer worker_data,
|
||||
gpointer *result,
|
||||
GError **error
|
||||
)
|
||||
{
|
||||
EvidenceIntegrityTaskData *task_data =
|
||||
worker_data;
|
||||
|
||||
EvidenceIntegrityVerificationResult *verification_result =
|
||||
NULL;
|
||||
|
||||
EvidenceIntegrityStatus integrity_status =
|
||||
EVIDENCE_INTEGRITY_STATUS_UNKNOWN;
|
||||
|
||||
GError *verify_error =
|
||||
NULL;
|
||||
|
||||
if (task == NULL ||
|
||||
cancellable == NULL ||
|
||||
task_data == NULL ||
|
||||
result == NULL ||
|
||||
error == NULL)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
*result =
|
||||
NULL;
|
||||
|
||||
if (g_cancellable_set_error_if_cancelled(
|
||||
cancellable,
|
||||
error
|
||||
))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
background_task_report_progress(
|
||||
task,
|
||||
0.10,
|
||||
"Préparation de la vérification"
|
||||
);
|
||||
|
||||
verification_result =
|
||||
evidence_integrity_verifier_verify(
|
||||
task_data->investigation_root_path,
|
||||
task_data->relative_path,
|
||||
task_data->expected_sha256,
|
||||
cancellable,
|
||||
&verify_error
|
||||
);
|
||||
|
||||
if (verification_result == NULL)
|
||||
{
|
||||
evidence_integrity_task_propagate_verify_error(
|
||||
error,
|
||||
verify_error
|
||||
);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
integrity_status =
|
||||
evidence_integrity_verification_result_get_status(
|
||||
verification_result
|
||||
);
|
||||
|
||||
background_task_report_progress(
|
||||
task,
|
||||
1.0,
|
||||
evidence_integrity_task_get_status_message(
|
||||
integrity_status
|
||||
)
|
||||
);
|
||||
|
||||
*result =
|
||||
verification_result;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Transmet une erreur secondaire au code appelant.
|
||||
*/
|
||||
static void evidence_integrity_task_propagate_start_error(
|
||||
GError **error,
|
||||
GError *start_error,
|
||||
const char *prefix
|
||||
)
|
||||
{
|
||||
if (start_error == NULL)
|
||||
{
|
||||
g_set_error_literal(
|
||||
error,
|
||||
EVIDENCE_INTEGRITY_TASK_ERROR,
|
||||
EVIDENCE_INTEGRITY_TASK_ERROR_VERIFY,
|
||||
"La tâche de vérification n'a pas pu être démarrée."
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (error != NULL)
|
||||
{
|
||||
g_propagate_prefixed_error(
|
||||
error,
|
||||
start_error,
|
||||
"%s",
|
||||
prefix
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
g_clear_error(
|
||||
&start_error
|
||||
);
|
||||
}
|
||||
|
||||
GQuark evidence_integrity_task_error_quark(void)
|
||||
{
|
||||
return g_quark_from_static_string(
|
||||
"evidence-integrity-task-error-quark"
|
||||
);
|
||||
}
|
||||
|
||||
BackgroundTask *evidence_integrity_task_start(
|
||||
TaskManager *task_manager,
|
||||
const EvidenceIntegrityTaskRequest *request,
|
||||
BackgroundTaskCompletionCallback completion_callback,
|
||||
gpointer completion_data,
|
||||
GDestroyNotify completion_data_destroy,
|
||||
GError **error
|
||||
)
|
||||
{
|
||||
EvidenceIntegrityTaskData *task_data =
|
||||
NULL;
|
||||
|
||||
BackgroundTask *task =
|
||||
NULL;
|
||||
|
||||
char *evidence_name =
|
||||
NULL;
|
||||
|
||||
char *task_title =
|
||||
NULL;
|
||||
|
||||
GError *start_error =
|
||||
NULL;
|
||||
|
||||
g_return_val_if_fail(
|
||||
error == NULL || *error == NULL,
|
||||
NULL
|
||||
);
|
||||
|
||||
if (task_manager == NULL ||
|
||||
!evidence_integrity_task_request_is_valid(
|
||||
request
|
||||
))
|
||||
{
|
||||
g_set_error_literal(
|
||||
error,
|
||||
EVIDENCE_INTEGRITY_TASK_ERROR,
|
||||
EVIDENCE_INTEGRITY_TASK_ERROR_INVALID_ARGUMENT,
|
||||
"Les paramètres de la tâche de vérification sont invalides."
|
||||
);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
task_data =
|
||||
evidence_integrity_task_data_new(
|
||||
request
|
||||
);
|
||||
|
||||
if (task_data == NULL)
|
||||
{
|
||||
g_set_error_literal(
|
||||
error,
|
||||
EVIDENCE_INTEGRITY_TASK_ERROR,
|
||||
EVIDENCE_INTEGRITY_TASK_ERROR_VERIFY,
|
||||
"Impossible d'allouer les données de la tâche "
|
||||
"de vérification."
|
||||
);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
evidence_name =
|
||||
g_path_get_basename(
|
||||
request->relative_path
|
||||
);
|
||||
|
||||
task_title =
|
||||
g_strdup_printf(
|
||||
"Vérifier l'intégrité : %s",
|
||||
evidence_name
|
||||
);
|
||||
|
||||
task =
|
||||
background_task_new(
|
||||
task_title
|
||||
);
|
||||
|
||||
g_free(
|
||||
task_title
|
||||
);
|
||||
|
||||
g_free(
|
||||
evidence_name
|
||||
);
|
||||
|
||||
if (task == NULL)
|
||||
{
|
||||
evidence_integrity_task_data_free(
|
||||
task_data
|
||||
);
|
||||
|
||||
g_set_error_literal(
|
||||
error,
|
||||
EVIDENCE_INTEGRITY_TASK_ERROR,
|
||||
EVIDENCE_INTEGRITY_TASK_ERROR_VERIFY,
|
||||
"Impossible de créer la tâche de vérification."
|
||||
);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!task_manager_add(
|
||||
task_manager,
|
||||
task,
|
||||
&start_error
|
||||
))
|
||||
{
|
||||
evidence_integrity_task_data_free(
|
||||
task_data
|
||||
);
|
||||
|
||||
background_task_unref(
|
||||
task
|
||||
);
|
||||
|
||||
evidence_integrity_task_propagate_start_error(
|
||||
error,
|
||||
start_error,
|
||||
"Impossible d'ajouter la tâche : "
|
||||
);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!background_task_start(
|
||||
task,
|
||||
evidence_integrity_task_worker,
|
||||
task_data,
|
||||
evidence_integrity_task_data_free,
|
||||
evidence_integrity_task_result_free,
|
||||
completion_callback,
|
||||
completion_data,
|
||||
completion_data_destroy,
|
||||
&start_error
|
||||
))
|
||||
{
|
||||
/*
|
||||
* background_task_start() n'a pas pris possession
|
||||
* de task_data ni de completion_data en cas d'échec.
|
||||
*/
|
||||
evidence_integrity_task_data_free(
|
||||
task_data
|
||||
);
|
||||
|
||||
task_manager_remove(
|
||||
task_manager,
|
||||
task
|
||||
);
|
||||
|
||||
background_task_unref(
|
||||
task
|
||||
);
|
||||
|
||||
evidence_integrity_task_propagate_start_error(
|
||||
error,
|
||||
start_error,
|
||||
"Impossible de démarrer la tâche : "
|
||||
);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return task;
|
||||
}
|
||||
|
||||
666
src/core/evidence_integrity_verifier.c
Normal file
666
src/core/evidence_integrity_verifier.c
Normal file
|
|
@ -0,0 +1,666 @@
|
|||
/******************************************************************************
|
||||
* @file evidence_integrity_verifier.c
|
||||
* @brief Vérification de l'intégrité d'une preuve numérique.
|
||||
******************************************************************************/
|
||||
|
||||
#define _XOPEN_SOURCE 700
|
||||
|
||||
#include "core/evidence_integrity_verifier.h"
|
||||
|
||||
#include "core/file_hash.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/**
|
||||
* @brief Résultat interne d'une vérification.
|
||||
*/
|
||||
struct EvidenceIntegrityVerificationResult
|
||||
{
|
||||
EvidenceIntegrityStatus status;
|
||||
|
||||
char *computed_sha256;
|
||||
guint64 size_bytes;
|
||||
|
||||
char *diagnostic;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Enregistre une erreur technique.
|
||||
*/
|
||||
static void evidence_integrity_verifier_set_error_literal(
|
||||
GError **error,
|
||||
EvidenceIntegrityVerifierError error_code,
|
||||
const char *message
|
||||
)
|
||||
{
|
||||
if (error == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
g_set_error_literal(
|
||||
error,
|
||||
EVIDENCE_INTEGRITY_VERIFIER_ERROR,
|
||||
error_code,
|
||||
message
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Vérifie qu'une empreinte SHA-256 est canonique.
|
||||
*/
|
||||
static gboolean evidence_integrity_verifier_sha256_is_valid(
|
||||
const char *sha256
|
||||
)
|
||||
{
|
||||
gsize character_index = 0;
|
||||
|
||||
if (sha256 == NULL ||
|
||||
strlen(sha256) != 64U)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
for (character_index = 0;
|
||||
character_index < 64U;
|
||||
character_index++)
|
||||
{
|
||||
if (!g_ascii_isdigit(sha256[character_index]) &&
|
||||
(
|
||||
sha256[character_index] < 'a' ||
|
||||
sha256[character_index] > 'f'
|
||||
))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Crée un résultat métier.
|
||||
*/
|
||||
static EvidenceIntegrityVerificationResult *
|
||||
evidence_integrity_verification_result_new(
|
||||
EvidenceIntegrityStatus status,
|
||||
const char *computed_sha256,
|
||||
guint64 size_bytes,
|
||||
const char *diagnostic,
|
||||
GError **error
|
||||
)
|
||||
{
|
||||
EvidenceIntegrityVerificationResult *result =
|
||||
NULL;
|
||||
|
||||
result =
|
||||
g_try_new0(
|
||||
EvidenceIntegrityVerificationResult,
|
||||
1
|
||||
);
|
||||
|
||||
if (result == NULL)
|
||||
{
|
||||
evidence_integrity_verifier_set_error_literal(
|
||||
error,
|
||||
EVIDENCE_INTEGRITY_VERIFIER_ERROR_MEMORY,
|
||||
"Impossible d'allouer le résultat de la vérification."
|
||||
);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
result->status =
|
||||
status;
|
||||
|
||||
result->size_bytes =
|
||||
size_bytes;
|
||||
|
||||
if (computed_sha256 != NULL)
|
||||
{
|
||||
result->computed_sha256 =
|
||||
g_strdup(
|
||||
computed_sha256
|
||||
);
|
||||
}
|
||||
|
||||
if (diagnostic != NULL)
|
||||
{
|
||||
result->diagnostic =
|
||||
g_strdup(
|
||||
diagnostic
|
||||
);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Vérifie si une annulation a été demandée.
|
||||
*/
|
||||
static gboolean evidence_integrity_verifier_is_cancelled(
|
||||
GCancellable *cancellable,
|
||||
GError **error
|
||||
)
|
||||
{
|
||||
if (cancellable == NULL ||
|
||||
!g_cancellable_is_cancelled(
|
||||
cancellable
|
||||
))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
evidence_integrity_verifier_set_error_literal(
|
||||
error,
|
||||
EVIDENCE_INTEGRITY_VERIFIER_ERROR_CANCELLED,
|
||||
"La vérification d'intégrité a été annulée."
|
||||
);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
GQuark evidence_integrity_verifier_error_quark(void)
|
||||
{
|
||||
return g_quark_from_static_string(
|
||||
"evidence-integrity-verifier-error-quark"
|
||||
);
|
||||
}
|
||||
|
||||
EvidenceIntegrityVerificationResult *
|
||||
evidence_integrity_verifier_verify(
|
||||
const char *investigation_root_path,
|
||||
const char *relative_path,
|
||||
const char *expected_sha256,
|
||||
GCancellable *cancellable,
|
||||
GError **error
|
||||
)
|
||||
{
|
||||
EvidenceIntegrityVerificationResult *result =
|
||||
NULL;
|
||||
|
||||
GError *hash_error =
|
||||
NULL;
|
||||
|
||||
char **path_components =
|
||||
NULL;
|
||||
|
||||
char *resolved_root_path =
|
||||
NULL;
|
||||
|
||||
char *current_path =
|
||||
NULL;
|
||||
|
||||
char *next_path =
|
||||
NULL;
|
||||
|
||||
char *computed_sha256 =
|
||||
NULL;
|
||||
|
||||
char *diagnostic =
|
||||
NULL;
|
||||
|
||||
struct stat root_status;
|
||||
struct stat path_status;
|
||||
|
||||
guint64 size_bytes =
|
||||
0;
|
||||
|
||||
guint component_index =
|
||||
0;
|
||||
|
||||
gboolean is_last_component =
|
||||
FALSE;
|
||||
|
||||
g_return_val_if_fail(
|
||||
error == NULL || *error == NULL,
|
||||
NULL
|
||||
);
|
||||
|
||||
if (investigation_root_path == NULL ||
|
||||
investigation_root_path[0] == '\0' ||
|
||||
relative_path == NULL ||
|
||||
relative_path[0] == '\0' ||
|
||||
!evidence_integrity_verifier_sha256_is_valid(
|
||||
expected_sha256
|
||||
))
|
||||
{
|
||||
evidence_integrity_verifier_set_error_literal(
|
||||
error,
|
||||
EVIDENCE_INTEGRITY_VERIFIER_ERROR_INVALID_ARGUMENT,
|
||||
"Les paramètres de la vérification sont invalides."
|
||||
);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (evidence_integrity_verifier_is_cancelled(
|
||||
cancellable,
|
||||
error
|
||||
))
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* La racine existe déjà lorsqu'une InvestigationSession est ouverte.
|
||||
* realpath() résout son éventuel lien symbolique et la canonicalise.
|
||||
*/
|
||||
resolved_root_path =
|
||||
realpath(
|
||||
investigation_root_path,
|
||||
NULL
|
||||
);
|
||||
|
||||
if (resolved_root_path == NULL)
|
||||
{
|
||||
g_set_error(
|
||||
error,
|
||||
EVIDENCE_INTEGRITY_VERIFIER_ERROR,
|
||||
EVIDENCE_INTEGRITY_VERIFIER_ERROR_ROOT,
|
||||
"Impossible de résoudre la racine '%s' : %s",
|
||||
investigation_root_path,
|
||||
g_strerror(errno)
|
||||
);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (stat(
|
||||
resolved_root_path,
|
||||
&root_status
|
||||
) != 0 ||
|
||||
!S_ISDIR(root_status.st_mode))
|
||||
{
|
||||
evidence_integrity_verifier_set_error_literal(
|
||||
error,
|
||||
EVIDENCE_INTEGRITY_VERIFIER_ERROR_ROOT,
|
||||
"La racine de l'enquête n'est pas un dossier valide."
|
||||
);
|
||||
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/*
|
||||
* Un chemin SQLite doit toujours être relatif à l'enquête.
|
||||
*/
|
||||
if (g_path_is_absolute(
|
||||
relative_path
|
||||
))
|
||||
{
|
||||
result =
|
||||
evidence_integrity_verification_result_new(
|
||||
EVIDENCE_INTEGRITY_STATUS_ERROR,
|
||||
NULL,
|
||||
0,
|
||||
"Le chemin enregistré est absolu.",
|
||||
error
|
||||
);
|
||||
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
path_components =
|
||||
g_strsplit(
|
||||
relative_path,
|
||||
G_DIR_SEPARATOR_S,
|
||||
-1
|
||||
);
|
||||
|
||||
if (path_components == NULL)
|
||||
{
|
||||
evidence_integrity_verifier_set_error_literal(
|
||||
error,
|
||||
EVIDENCE_INTEGRITY_VERIFIER_ERROR_MEMORY,
|
||||
"Impossible de décomposer le chemin de la preuve."
|
||||
);
|
||||
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
current_path =
|
||||
g_strdup(
|
||||
resolved_root_path
|
||||
);
|
||||
|
||||
if (current_path == NULL)
|
||||
{
|
||||
evidence_integrity_verifier_set_error_literal(
|
||||
error,
|
||||
EVIDENCE_INTEGRITY_VERIFIER_ERROR_MEMORY,
|
||||
"Impossible de conserver le chemin de la racine."
|
||||
);
|
||||
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
for (component_index = 0;
|
||||
path_components[component_index] != NULL;
|
||||
component_index++)
|
||||
{
|
||||
const char *component =
|
||||
path_components[component_index];
|
||||
|
||||
is_last_component =
|
||||
path_components[component_index + 1U] == NULL;
|
||||
|
||||
/*
|
||||
* Le refus explicite évite toute traversée hors de la racine.
|
||||
*/
|
||||
if (component[0] == '\0' ||
|
||||
g_strcmp0(component, ".") == 0 ||
|
||||
g_strcmp0(component, "..") == 0)
|
||||
{
|
||||
result =
|
||||
evidence_integrity_verification_result_new(
|
||||
EVIDENCE_INTEGRITY_STATUS_ERROR,
|
||||
NULL,
|
||||
0,
|
||||
"Le chemin contient un composant interdit.",
|
||||
error
|
||||
);
|
||||
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
next_path =
|
||||
g_build_filename(
|
||||
current_path,
|
||||
component,
|
||||
NULL
|
||||
);
|
||||
|
||||
if (next_path == NULL)
|
||||
{
|
||||
evidence_integrity_verifier_set_error_literal(
|
||||
error,
|
||||
EVIDENCE_INTEGRITY_VERIFIER_ERROR_MEMORY,
|
||||
"Impossible de construire le chemin de la preuve."
|
||||
);
|
||||
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
g_free(
|
||||
current_path
|
||||
);
|
||||
|
||||
current_path =
|
||||
next_path;
|
||||
|
||||
next_path =
|
||||
NULL;
|
||||
|
||||
if (lstat(
|
||||
current_path,
|
||||
&path_status
|
||||
) != 0)
|
||||
{
|
||||
if (errno == ENOENT ||
|
||||
errno == ENOTDIR)
|
||||
{
|
||||
result =
|
||||
evidence_integrity_verification_result_new(
|
||||
EVIDENCE_INTEGRITY_STATUS_MISSING,
|
||||
NULL,
|
||||
0,
|
||||
"Le fichier de preuve est absent.",
|
||||
error
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
diagnostic =
|
||||
g_strdup_printf(
|
||||
"Impossible d'inspecter '%s' : %s",
|
||||
current_path,
|
||||
g_strerror(errno)
|
||||
);
|
||||
|
||||
result =
|
||||
evidence_integrity_verification_result_new(
|
||||
EVIDENCE_INTEGRITY_STATUS_ERROR,
|
||||
NULL,
|
||||
0,
|
||||
diagnostic,
|
||||
error
|
||||
);
|
||||
}
|
||||
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/*
|
||||
* Tous les liens symboliques sont refusés, y compris lorsqu'ils
|
||||
* semblent rester à l'intérieur de l'enquête.
|
||||
*/
|
||||
if (S_ISLNK(path_status.st_mode))
|
||||
{
|
||||
result =
|
||||
evidence_integrity_verification_result_new(
|
||||
EVIDENCE_INTEGRITY_STATUS_ERROR,
|
||||
NULL,
|
||||
0,
|
||||
"Le chemin de la preuve contient un lien symbolique.",
|
||||
error
|
||||
);
|
||||
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (!is_last_component &&
|
||||
!S_ISDIR(path_status.st_mode))
|
||||
{
|
||||
result =
|
||||
evidence_integrity_verification_result_new(
|
||||
EVIDENCE_INTEGRITY_STATUS_ERROR,
|
||||
NULL,
|
||||
0,
|
||||
"Un composant intermédiaire du chemin "
|
||||
"n'est pas un dossier.",
|
||||
error
|
||||
);
|
||||
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
if (evidence_integrity_verifier_is_cancelled(
|
||||
cancellable,
|
||||
error
|
||||
))
|
||||
{
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (!file_hash_compute_sha256(
|
||||
current_path,
|
||||
cancellable,
|
||||
&computed_sha256,
|
||||
&size_bytes,
|
||||
&hash_error
|
||||
))
|
||||
{
|
||||
if (g_error_matches(
|
||||
hash_error,
|
||||
FILE_HASH_ERROR,
|
||||
FILE_HASH_ERROR_CANCELLED
|
||||
))
|
||||
{
|
||||
evidence_integrity_verifier_set_error_literal(
|
||||
error,
|
||||
EVIDENCE_INTEGRITY_VERIFIER_ERROR_CANCELLED,
|
||||
"La vérification d'intégrité a été annulée."
|
||||
);
|
||||
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (g_error_matches(
|
||||
hash_error,
|
||||
FILE_HASH_ERROR,
|
||||
FILE_HASH_ERROR_NOT_FOUND
|
||||
))
|
||||
{
|
||||
result =
|
||||
evidence_integrity_verification_result_new(
|
||||
EVIDENCE_INTEGRITY_STATUS_MISSING,
|
||||
NULL,
|
||||
0,
|
||||
hash_error->message,
|
||||
error
|
||||
);
|
||||
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
result =
|
||||
evidence_integrity_verification_result_new(
|
||||
EVIDENCE_INTEGRITY_STATUS_ERROR,
|
||||
NULL,
|
||||
0,
|
||||
hash_error != NULL
|
||||
? hash_error->message
|
||||
: "Le calcul SHA-256 a échoué.",
|
||||
error
|
||||
);
|
||||
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (strcmp(
|
||||
computed_sha256,
|
||||
expected_sha256
|
||||
) == 0)
|
||||
{
|
||||
result =
|
||||
evidence_integrity_verification_result_new(
|
||||
EVIDENCE_INTEGRITY_STATUS_VALID,
|
||||
computed_sha256,
|
||||
size_bytes,
|
||||
NULL,
|
||||
error
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
result =
|
||||
evidence_integrity_verification_result_new(
|
||||
EVIDENCE_INTEGRITY_STATUS_MODIFIED,
|
||||
computed_sha256,
|
||||
size_bytes,
|
||||
"L'empreinte recalculée diffère "
|
||||
"de l'empreinte enregistrée.",
|
||||
error
|
||||
);
|
||||
}
|
||||
|
||||
cleanup:
|
||||
|
||||
g_clear_error(
|
||||
&hash_error
|
||||
);
|
||||
|
||||
g_free(
|
||||
diagnostic
|
||||
);
|
||||
|
||||
g_free(
|
||||
computed_sha256
|
||||
);
|
||||
|
||||
g_free(
|
||||
next_path
|
||||
);
|
||||
|
||||
g_free(
|
||||
current_path
|
||||
);
|
||||
|
||||
g_strfreev(
|
||||
path_components
|
||||
);
|
||||
|
||||
free(
|
||||
resolved_root_path
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
EvidenceIntegrityStatus
|
||||
evidence_integrity_verification_result_get_status(
|
||||
const EvidenceIntegrityVerificationResult *result
|
||||
)
|
||||
{
|
||||
if (result == NULL)
|
||||
{
|
||||
return EVIDENCE_INTEGRITY_STATUS_ERROR;
|
||||
}
|
||||
|
||||
return result->status;
|
||||
}
|
||||
|
||||
const char *
|
||||
evidence_integrity_verification_result_get_computed_sha256(
|
||||
const EvidenceIntegrityVerificationResult *result
|
||||
)
|
||||
{
|
||||
if (result == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return result->computed_sha256;
|
||||
}
|
||||
|
||||
guint64 evidence_integrity_verification_result_get_size_bytes(
|
||||
const EvidenceIntegrityVerificationResult *result
|
||||
)
|
||||
{
|
||||
if (result == NULL)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return result->size_bytes;
|
||||
}
|
||||
|
||||
const char *
|
||||
evidence_integrity_verification_result_get_diagnostic(
|
||||
const EvidenceIntegrityVerificationResult *result
|
||||
)
|
||||
{
|
||||
if (result == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return result->diagnostic;
|
||||
}
|
||||
|
||||
void evidence_integrity_verification_result_free(
|
||||
EvidenceIntegrityVerificationResult *result
|
||||
)
|
||||
{
|
||||
if (result == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
g_free(
|
||||
result->diagnostic
|
||||
);
|
||||
|
||||
g_free(
|
||||
result->computed_sha256
|
||||
);
|
||||
|
||||
g_free(
|
||||
result
|
||||
);
|
||||
}
|
||||
|
|
@ -148,6 +148,14 @@ static const char *const evidence_dao_list_all_sql =
|
|||
" preuves.imported_at ASC,"
|
||||
" preuves.id ASC;";
|
||||
|
||||
/**
|
||||
* @brief Requête de mise à jour du statut d'intégrité.
|
||||
*/
|
||||
static const char *const evidence_dao_update_integrity_status_sql =
|
||||
"UPDATE preuves "
|
||||
"SET integrity_status = ? "
|
||||
"WHERE id = ?;";
|
||||
|
||||
/**
|
||||
* @brief Enregistre une erreur littérale.
|
||||
*/
|
||||
|
|
@ -1191,6 +1199,151 @@ cleanup:
|
|||
return evidence_record;
|
||||
}
|
||||
|
||||
gboolean evidence_dao_update_integrity_status(
|
||||
EvidenceDao *evidence_dao,
|
||||
const char *identifier,
|
||||
EvidenceIntegrityStatus integrity_status,
|
||||
GError **error
|
||||
)
|
||||
{
|
||||
DatabaseStatement *statement =
|
||||
NULL;
|
||||
|
||||
gboolean identifier_exists =
|
||||
FALSE;
|
||||
|
||||
gboolean success =
|
||||
FALSE;
|
||||
|
||||
g_return_val_if_fail(
|
||||
error == NULL || *error == NULL,
|
||||
FALSE
|
||||
);
|
||||
|
||||
if (evidence_dao == NULL ||
|
||||
evidence_dao->database == NULL ||
|
||||
identifier == NULL ||
|
||||
!g_uuid_string_is_valid(
|
||||
identifier
|
||||
))
|
||||
{
|
||||
evidence_dao_set_error_literal(
|
||||
error,
|
||||
EVIDENCE_DAO_ERROR_INVALID_ARGUMENT,
|
||||
"Le DAO ou l'identifiant de preuve est invalide."
|
||||
);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (integrity_status <
|
||||
EVIDENCE_INTEGRITY_STATUS_UNKNOWN ||
|
||||
integrity_status >
|
||||
EVIDENCE_INTEGRITY_STATUS_ERROR)
|
||||
{
|
||||
evidence_dao_set_error_literal(
|
||||
error,
|
||||
EVIDENCE_DAO_ERROR_INVALID_ARGUMENT,
|
||||
"Le statut d'intégrité demandé est invalide."
|
||||
);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Cette vérification permet de distinguer une preuve absente
|
||||
* d'une mise à jour SQLite ayant réellement échoué.
|
||||
*/
|
||||
if (!evidence_dao_value_exists(
|
||||
evidence_dao,
|
||||
evidence_dao_identifier_exists_sql,
|
||||
identifier,
|
||||
&identifier_exists,
|
||||
error
|
||||
))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!identifier_exists)
|
||||
{
|
||||
evidence_dao_set_error_literal(
|
||||
error,
|
||||
EVIDENCE_DAO_ERROR_NOT_FOUND,
|
||||
"La preuve à mettre à jour n'existe pas."
|
||||
);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
statement =
|
||||
database_statement_prepare(
|
||||
evidence_dao->database,
|
||||
evidence_dao_update_integrity_status_sql
|
||||
);
|
||||
|
||||
if (statement == NULL)
|
||||
{
|
||||
evidence_dao_set_database_error(
|
||||
evidence_dao,
|
||||
error,
|
||||
EVIDENCE_DAO_ERROR_PREPARE,
|
||||
"Impossible de préparer la mise à jour "
|
||||
"du statut d'intégrité"
|
||||
);
|
||||
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (!database_statement_bind_int64(
|
||||
statement,
|
||||
1,
|
||||
(int64_t) integrity_status
|
||||
) ||
|
||||
!database_statement_bind_text(
|
||||
statement,
|
||||
2,
|
||||
identifier
|
||||
))
|
||||
{
|
||||
evidence_dao_set_database_error(
|
||||
evidence_dao,
|
||||
error,
|
||||
EVIDENCE_DAO_ERROR_BIND,
|
||||
"Impossible de lier les paramètres "
|
||||
"du statut d'intégrité"
|
||||
);
|
||||
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (database_statement_step(
|
||||
statement
|
||||
) != DATABASE_STATEMENT_STEP_DONE)
|
||||
{
|
||||
evidence_dao_set_database_error(
|
||||
evidence_dao,
|
||||
error,
|
||||
EVIDENCE_DAO_ERROR_EXECUTE,
|
||||
"Impossible de mettre à jour "
|
||||
"le statut d'intégrité"
|
||||
);
|
||||
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
success =
|
||||
TRUE;
|
||||
|
||||
cleanup:
|
||||
|
||||
database_statement_finalize(
|
||||
statement
|
||||
);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
gboolean evidence_dao_count(
|
||||
EvidenceDao *evidence_dao,
|
||||
guint64 *out_count,
|
||||
|
|
|
|||
|
|
@ -468,6 +468,75 @@ static TestEvidenceDaoFixture test_evidence_dao_fixture_create(void)
|
|||
return fixture;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Crée une preuve destinée aux tests de statut d'intégrité.
|
||||
*/
|
||||
static EvidenceRecord *test_evidence_dao_create_integrity_record(
|
||||
const char *identifier,
|
||||
EvidenceIntegrityStatus integrity_status
|
||||
)
|
||||
{
|
||||
EvidenceRecord *evidence_record =
|
||||
NULL;
|
||||
|
||||
char *internal_name =
|
||||
NULL;
|
||||
|
||||
char *relative_path =
|
||||
NULL;
|
||||
|
||||
GError *error =
|
||||
NULL;
|
||||
|
||||
assert(identifier != NULL);
|
||||
|
||||
internal_name =
|
||||
g_strdup_printf(
|
||||
"%s.bin",
|
||||
identifier
|
||||
);
|
||||
|
||||
relative_path =
|
||||
g_strdup_printf(
|
||||
"01_Preuves_Originales/Documents/%s.bin",
|
||||
identifier
|
||||
);
|
||||
|
||||
assert(internal_name != NULL);
|
||||
assert(relative_path != NULL);
|
||||
|
||||
evidence_record =
|
||||
evidence_record_new(
|
||||
identifier,
|
||||
"preuve_integrite.bin",
|
||||
internal_name,
|
||||
relative_path,
|
||||
"document",
|
||||
128,
|
||||
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
||||
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
"2026-07-20T10:00:00Z",
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
integrity_status,
|
||||
&error
|
||||
);
|
||||
|
||||
assert(evidence_record != NULL);
|
||||
assert(error == NULL);
|
||||
|
||||
g_free(
|
||||
relative_path
|
||||
);
|
||||
|
||||
g_free(
|
||||
internal_name
|
||||
);
|
||||
|
||||
return evidence_record;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Détruit une fixture et ses fichiers temporaires.
|
||||
*/
|
||||
|
|
@ -1743,13 +1812,315 @@ static void test_evidence_dao_list_invalid_arguments(void)
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Vérifie la mise à jour d'une preuve vers VALID.
|
||||
*/
|
||||
static void test_evidence_dao_update_integrity_valid(void)
|
||||
{
|
||||
TestEvidenceDaoFixture fixture =
|
||||
test_evidence_dao_fixture_create();
|
||||
|
||||
EvidenceRecord *inserted_record =
|
||||
NULL;
|
||||
|
||||
EvidenceRecord *loaded_record =
|
||||
NULL;
|
||||
|
||||
GError *error =
|
||||
NULL;
|
||||
|
||||
const char *identifier =
|
||||
"12345678-1111-4111-8111-111111111111";
|
||||
|
||||
inserted_record =
|
||||
test_evidence_dao_create_integrity_record(
|
||||
identifier,
|
||||
EVIDENCE_INTEGRITY_STATUS_UNKNOWN
|
||||
);
|
||||
|
||||
assert(
|
||||
evidence_dao_insert(
|
||||
fixture.evidence_dao,
|
||||
inserted_record,
|
||||
&error
|
||||
)
|
||||
);
|
||||
|
||||
assert(error == NULL);
|
||||
|
||||
assert(
|
||||
evidence_dao_update_integrity_status(
|
||||
fixture.evidence_dao,
|
||||
identifier,
|
||||
EVIDENCE_INTEGRITY_STATUS_VALID,
|
||||
&error
|
||||
)
|
||||
);
|
||||
|
||||
assert(error == NULL);
|
||||
|
||||
loaded_record =
|
||||
evidence_dao_find_by_identifier(
|
||||
fixture.evidence_dao,
|
||||
identifier,
|
||||
&error
|
||||
);
|
||||
|
||||
assert(loaded_record != NULL);
|
||||
assert(error == NULL);
|
||||
|
||||
assert(
|
||||
evidence_record_get_integrity_status(
|
||||
loaded_record
|
||||
) == EVIDENCE_INTEGRITY_STATUS_VALID
|
||||
);
|
||||
|
||||
evidence_record_free(
|
||||
loaded_record
|
||||
);
|
||||
|
||||
evidence_record_free(
|
||||
inserted_record
|
||||
);
|
||||
|
||||
test_evidence_dao_fixture_clear(
|
||||
&fixture
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Vérifie la mise à jour d'une preuve vers MODIFIED.
|
||||
*/
|
||||
static void test_evidence_dao_update_integrity_modified(void)
|
||||
{
|
||||
TestEvidenceDaoFixture fixture =
|
||||
test_evidence_dao_fixture_create();
|
||||
|
||||
EvidenceRecord *inserted_record =
|
||||
NULL;
|
||||
|
||||
EvidenceRecord *loaded_record =
|
||||
NULL;
|
||||
|
||||
GError *error =
|
||||
NULL;
|
||||
|
||||
const char *identifier =
|
||||
"12345678-2222-4222-8222-222222222222";
|
||||
|
||||
inserted_record =
|
||||
test_evidence_dao_create_integrity_record(
|
||||
identifier,
|
||||
EVIDENCE_INTEGRITY_STATUS_VALID
|
||||
);
|
||||
|
||||
assert(
|
||||
evidence_dao_insert(
|
||||
fixture.evidence_dao,
|
||||
inserted_record,
|
||||
&error
|
||||
)
|
||||
);
|
||||
|
||||
assert(error == NULL);
|
||||
|
||||
assert(
|
||||
evidence_dao_update_integrity_status(
|
||||
fixture.evidence_dao,
|
||||
identifier,
|
||||
EVIDENCE_INTEGRITY_STATUS_MODIFIED,
|
||||
&error
|
||||
)
|
||||
);
|
||||
|
||||
assert(error == NULL);
|
||||
|
||||
loaded_record =
|
||||
evidence_dao_find_by_identifier(
|
||||
fixture.evidence_dao,
|
||||
identifier,
|
||||
&error
|
||||
);
|
||||
|
||||
assert(loaded_record != NULL);
|
||||
assert(error == NULL);
|
||||
|
||||
assert(
|
||||
evidence_record_get_integrity_status(
|
||||
loaded_record
|
||||
) == EVIDENCE_INTEGRITY_STATUS_MODIFIED
|
||||
);
|
||||
|
||||
evidence_record_free(
|
||||
loaded_record
|
||||
);
|
||||
|
||||
evidence_record_free(
|
||||
inserted_record
|
||||
);
|
||||
|
||||
test_evidence_dao_fixture_clear(
|
||||
&fixture
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Vérifie le signalement d'une preuve inexistante.
|
||||
*/
|
||||
static void test_evidence_dao_update_integrity_missing(void)
|
||||
{
|
||||
TestEvidenceDaoFixture fixture =
|
||||
test_evidence_dao_fixture_create();
|
||||
|
||||
GError *error =
|
||||
NULL;
|
||||
|
||||
assert(
|
||||
!evidence_dao_update_integrity_status(
|
||||
fixture.evidence_dao,
|
||||
"12345678-3333-4333-8333-333333333333",
|
||||
EVIDENCE_INTEGRITY_STATUS_VALID,
|
||||
&error
|
||||
)
|
||||
);
|
||||
|
||||
assert(error != NULL);
|
||||
assert(error->domain == EVIDENCE_DAO_ERROR);
|
||||
|
||||
assert(
|
||||
error->code ==
|
||||
(gint) EVIDENCE_DAO_ERROR_NOT_FOUND
|
||||
);
|
||||
|
||||
g_clear_error(
|
||||
&error
|
||||
);
|
||||
|
||||
test_evidence_dao_fixture_clear(
|
||||
&fixture
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Vérifie le refus d'un statut hors de l'énumération.
|
||||
*/
|
||||
static void test_evidence_dao_update_integrity_invalid_status(void)
|
||||
{
|
||||
TestEvidenceDaoFixture fixture =
|
||||
test_evidence_dao_fixture_create();
|
||||
|
||||
GError *error =
|
||||
NULL;
|
||||
|
||||
assert(
|
||||
!evidence_dao_update_integrity_status(
|
||||
fixture.evidence_dao,
|
||||
"12345678-4444-4444-8444-444444444444",
|
||||
(EvidenceIntegrityStatus)
|
||||
(EVIDENCE_INTEGRITY_STATUS_ERROR + 1),
|
||||
&error
|
||||
)
|
||||
);
|
||||
|
||||
assert(error != NULL);
|
||||
assert(error->domain == EVIDENCE_DAO_ERROR);
|
||||
|
||||
assert(
|
||||
error->code ==
|
||||
(gint) EVIDENCE_DAO_ERROR_INVALID_ARGUMENT
|
||||
);
|
||||
|
||||
g_clear_error(
|
||||
&error
|
||||
);
|
||||
|
||||
test_evidence_dao_fixture_clear(
|
||||
&fixture
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Vérifie le refus des arguments invalides.
|
||||
*/
|
||||
static void test_evidence_dao_update_integrity_invalid_arguments(void)
|
||||
{
|
||||
TestEvidenceDaoFixture fixture =
|
||||
test_evidence_dao_fixture_create();
|
||||
|
||||
GError *error =
|
||||
NULL;
|
||||
|
||||
assert(
|
||||
!evidence_dao_update_integrity_status(
|
||||
NULL,
|
||||
"12345678-5555-4555-8555-555555555555",
|
||||
EVIDENCE_INTEGRITY_STATUS_VALID,
|
||||
&error
|
||||
)
|
||||
);
|
||||
|
||||
assert(error != NULL);
|
||||
|
||||
assert(
|
||||
error->code ==
|
||||
(gint) EVIDENCE_DAO_ERROR_INVALID_ARGUMENT
|
||||
);
|
||||
|
||||
g_clear_error(
|
||||
&error
|
||||
);
|
||||
|
||||
assert(
|
||||
!evidence_dao_update_integrity_status(
|
||||
fixture.evidence_dao,
|
||||
NULL,
|
||||
EVIDENCE_INTEGRITY_STATUS_VALID,
|
||||
&error
|
||||
)
|
||||
);
|
||||
|
||||
assert(error != NULL);
|
||||
|
||||
assert(
|
||||
error->code ==
|
||||
(gint) EVIDENCE_DAO_ERROR_INVALID_ARGUMENT
|
||||
);
|
||||
|
||||
g_clear_error(
|
||||
&error
|
||||
);
|
||||
|
||||
assert(
|
||||
!evidence_dao_update_integrity_status(
|
||||
fixture.evidence_dao,
|
||||
"identifiant-invalide",
|
||||
EVIDENCE_INTEGRITY_STATUS_VALID,
|
||||
&error
|
||||
)
|
||||
);
|
||||
|
||||
assert(error != NULL);
|
||||
|
||||
assert(
|
||||
error->code ==
|
||||
(gint) EVIDENCE_DAO_ERROR_INVALID_ARGUMENT
|
||||
);
|
||||
|
||||
g_clear_error(
|
||||
&error
|
||||
);
|
||||
|
||||
test_evidence_dao_fixture_clear(
|
||||
&fixture
|
||||
);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
test_evidence_dao_new_null_database();
|
||||
test_evidence_dao_new_valid();
|
||||
test_evidence_dao_free_null();
|
||||
test_evidence_dao_insert_valid_full();
|
||||
test_evidence_dao_insert_valid_full();
|
||||
test_evidence_dao_insert_optional_null();
|
||||
test_evidence_dao_insert_duplicate_sha256();
|
||||
test_evidence_dao_insert_duplicate_identifier();
|
||||
|
|
@ -1764,6 +2135,11 @@ int main(void)
|
|||
test_evidence_dao_list_empty();
|
||||
test_evidence_dao_list_order();
|
||||
test_evidence_dao_list_invalid_arguments();
|
||||
test_evidence_dao_update_integrity_valid();
|
||||
test_evidence_dao_update_integrity_modified();
|
||||
test_evidence_dao_update_integrity_missing();
|
||||
test_evidence_dao_update_integrity_invalid_status();
|
||||
test_evidence_dao_update_integrity_invalid_arguments();
|
||||
|
||||
printf(
|
||||
"EvidenceDao : tests de construction valides.\n"
|
||||
|
|
|
|||
1321
tests/test_evidence_integrity_task.c
Normal file
1321
tests/test_evidence_integrity_task.c
Normal file
File diff suppressed because it is too large
Load diff
1016
tests/test_evidence_integrity_verifier.c
Normal file
1016
tests/test_evidence_integrity_verifier.c
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue