summaryrefslogtreecommitdiff
path: root/src/analysis/db
diff options
context:
space:
mode:
Diffstat (limited to 'src/analysis/db')
-rw-r--r--src/analysis/db/client.c56
-rw-r--r--src/analysis/db/client.h4
-rw-r--r--src/analysis/db/keymgn.c121
-rw-r--r--src/analysis/db/keymgn.h13
-rw-r--r--src/analysis/db/misc/rlestr.h2
-rw-r--r--src/analysis/db/server.c169
-rw-r--r--src/analysis/db/server.h2
7 files changed, 340 insertions, 27 deletions
diff --git a/src/analysis/db/client.c b/src/analysis/db/client.c
index 2a5a185..bb900ed 100644
--- a/src/analysis/db/client.c
+++ b/src/analysis/db/client.c
@@ -35,6 +35,7 @@
#include <i18n.h>
+#include "keymgn.h"
#include "protocol.h"
#include "misc/rlestr.h"
#include "../../common/io.h"
@@ -47,6 +48,9 @@ struct _GDbClient
{
GObject parent; /* A laisser en premier */
+ char *author; /* Utilisateur représenté */
+ char *key_file; /* Accès sa la clef privée */
+
const char *name; /* Désignation du binaire */
rle_string hash; /* Empreinte du binaire lié */
@@ -141,6 +145,9 @@ static void g_db_client_init(GDbClient *client)
static void g_db_client_finalize(GDbClient *client)
{
+ free(client->author);
+ free(client->key_file);
+
unset_rle_string(&client->hash);
G_OBJECT_CLASS(g_db_client_parent_class)->finalize(G_OBJECT(client));
@@ -150,24 +157,29 @@ static void g_db_client_finalize(GDbClient *client)
/******************************************************************************
* *
-* Paramètres : name = désignation humaine du binaire associé. *
+* Paramètres : author = utilisateur à représenter via le client. *
+* kfile = clef menant à sa clef privée. *
+* name = désignation humaine du binaire associé. *
* 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 plae ou NULL en cas d'échec. *
+* Retour : Structure mise en place ou NULL en cas d'échec. *
* *
* Remarques : - *
* *
******************************************************************************/
-GDbClient *g_db_client_new(const char *name, const char *hash, GList *collections)
+GDbClient *g_db_client_new(char *author, char *kfile, const char *name, const char *hash, GList *collections)
{
GDbClient *result; /* Adresse à retourner */
result = g_object_new(G_TYPE_DB_CLIENT, NULL);
+ result->author = author;
+ result->key_file = kfile;
+
result->name = name;
set_rle_string(&result->hash, hash);
@@ -180,10 +192,9 @@ GDbClient *g_db_client_new(const char *name, const char *hash, GList *collection
/******************************************************************************
* *
-* Paramètres : client = client pour les accès distants à manipuler. *
-* host = hôte à représenter pour le service. *
-* port = port de connexion pour les clients. *
-* username = utilisateur effectuant les évolutions. *
+* Paramètres : client = client pour les accès distants à manipuler. *
+* host = hôte à représenter pour le service. *
+* port = port de connexion pour les clients. *
* *
* Description : Démarre la connexion à la base de données. *
* *
@@ -193,12 +204,16 @@ GDbClient *g_db_client_new(const char *name, const char *hash, GList *collection
* *
******************************************************************************/
-bool g_db_client_start(GDbClient *client, const char *host, unsigned short port, const char *username)
+bool g_db_client_start(GDbClient *client, const char *host, unsigned short port)
{
struct hostent *hp; /* Informations sur l'hôte */
struct sockaddr_in addr; /* Adresse de transmission */
int ret; /* Bilan d'un appel */
rle_string user; /* Nom d'utilisateur associé */
+ GChecksum *checksum; /* Empreinte MD5 à signer */
+ unsigned char md5_digest[16]; /* Empreinte MD5 calculée */
+ RSA *key; /* Clef pour la signature */
+ unsigned char sig[RSA_USED_SIZE]; /* Signature effectuée */
uint32_t data; /* Mot de données lues */
DBError error; /* Validation de la connexion */
@@ -228,16 +243,13 @@ bool g_db_client_start(GDbClient *client, const char *host, unsigned short port,
goto gdcs_no_listening;
}
- /* Préparation du nom d'utilisateur à diffuser */
-
- init_rle_string(&user, username);
-
/**
* 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é.
* - l'identifiant de l'utilisateur effectuant des modifications.
+ * - la signature de l'empreinte MD5 de l'identifiant.
*
* Tout ceci est à synchroniser avec la fonction g_db_server_listener().
*/
@@ -251,7 +263,25 @@ bool g_db_client_start(GDbClient *client, const char *host, unsigned short port,
if (!send_rle_string(&client->hash, client->fd, MSG_MORE))
goto gdcs_error;
- if (!send_rle_string(&user, client->fd, 0))
+ init_rle_string(&user, client->author);
+
+ if (!send_rle_string(&user, client->fd, MSG_MORE))
+ goto gdcs_error;
+
+ checksum = g_checksum_new(G_CHECKSUM_MD5);
+ g_checksum_update(checksum, (guchar *)get_rle_string(&user), get_rle_length(&user));
+ g_checksum_get_digest(checksum, (guint8 *)md5_digest, (gsize []) { sizeof(md5_digest) });
+ g_checksum_free(checksum);
+
+ key = load_rsa_key(client->key_file, true);
+ if (key == NULL) goto gdcs_error;
+
+ if (!sign_md5_hash(key, md5_digest, sig))
+ goto gdcs_error;
+
+ RSA_free(key);
+
+ if (!safe_send(client->fd, sig, RSA_USED_SIZE, 0))
goto gdcs_error;
/**
diff --git a/src/analysis/db/client.h b/src/analysis/db/client.h
index 0304786..749b8aa 100644
--- a/src/analysis/db/client.h
+++ b/src/analysis/db/client.h
@@ -51,10 +51,10 @@ typedef struct _GDbClientClass GDbClientClass;
GType g_db_client_get_type(void);
/* Prépare un client pour une connexion à une BD. */
-GDbClient *g_db_client_new(const char *, const char *, GList *);
+GDbClient *g_db_client_new(char *, char *, const char *, const char *, GList *);
/* Démarre la connexion à la base de données. */
-bool g_db_client_start(GDbClient *, const char *, unsigned short, const char *);
+bool g_db_client_start(GDbClient *, const char *, unsigned short);
/* Arrête la connexion à la base de données. */
void g_db_client_stop(GDbClient *);
diff --git a/src/analysis/db/keymgn.c b/src/analysis/db/keymgn.c
index bcd8d28..bce2ce8 100644
--- a/src/analysis/db/keymgn.c
+++ b/src/analysis/db/keymgn.c
@@ -24,12 +24,14 @@
#include "keymgn.h"
+#include <assert.h>
#include <glib.h>
#include <malloc.h>
#include <stdio.h>
#include <unistd.h>
+#include <openssl/err.h>
#include <openssl/evp.h>
-#include <openssl/rsa.h>
+#include <openssl/pem.h>
#include <i18n.h>
@@ -79,7 +81,7 @@ bool ensure_user_has_rsa_keys(void)
result = generate_user_rsa_keys(priv, pub);
if (!result)
- fprintf(stderr, _("Unable to create new user RSA key pair."));
+ fprintf(stderr, _("Unable to create new user RSA key pair.\n"));
}
@@ -110,7 +112,6 @@ static bool generate_user_rsa_keys(const char *priv, const char *pub)
EVP_PKEY_CTX *ctx; /* Contexte de génération */
int ret; /* Bilan d'un appel */
EVP_PKEY *pair; /* Paire de clefs RSA générée */
- char *filename; /* Chemin d'accès */
FILE *stream; /* Flux ouvert en écriture */
result = false;
@@ -125,7 +126,7 @@ static bool generate_user_rsa_keys(const char *priv, const char *pub)
ret = EVP_PKEY_keygen_init(ctx);
if (ret != 1) goto euhrk_exit;
- ret = EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, 2048);
+ ret = EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, RSA_USED_SIZE * 8);
if (ret != 1) goto euhrk_exit;
ret = EVP_PKEY_keygen(ctx, &pair);
@@ -164,3 +165,115 @@ static bool generate_user_rsa_keys(const char *priv, const char *pub)
return result;
}
+
+
+/******************************************************************************
+* *
+* Paramètres : filename = chemin d'accès à la clef à charger. *
+* private = nature de la clef visée. *
+* *
+* Description : Charge une clef RSA à partir d'un fichier PEM. *
+* *
+* Retour : Clef RSA ou NULL en cas de soucis. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+RSA *load_rsa_key(const char *filename, bool private)
+{
+ RSA *result; /* Clef à retourner */
+ FILE *stream; /* Flux ouvert en lecture */
+ int bits; /* Taille de la clef en bits */
+
+ result = NULL;
+
+ stream = fopen(filename, "r");
+ if (stream == NULL) goto lrk_exit;
+
+ if (private)
+ result = PEM_read_RSAPrivateKey(stream, &result, NULL, NULL);
+ else
+ result = PEM_read_RSA_PUBKEY(stream, &result, NULL, NULL);
+
+ fclose(stream);
+
+ if (result == NULL)
+ fprintf(stderr, _("Unable to read the RSA key from '%s'.\n"), filename);
+
+ else
+ {
+ bits = RSA_size(result);
+
+ if (bits != RSA_USED_SIZE)
+ {
+ fprintf(stderr, _("Wrong RSA key size for %s: expected %d, got %d.\n"), filename, RSA_USED_SIZE, bits);
+ RSA_free(result);
+ result = NULL;
+ }
+
+ }
+
+ lrk_exit:
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : key = clef RSA à utiliser. *
+* hash = empreinte à signer. *
+* sig = signature calculée. *
+* *
+* Description : Signe une empreinte MD5 à l'aide d'une clef RSA. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool sign_md5_hash(RSA *key, const unsigned char *hash, unsigned char *sig)
+{
+ int ret; /* Bilan de l'opération */
+ unsigned int siglen; /* Taille de la signature */
+
+ siglen = RSA_USED_SIZE;
+
+ ret = RSA_sign(NID_md5, hash, 16, sig, &siglen, key);
+
+ assert(siglen == RSA_USED_SIZE);
+
+ if (ret != 1)
+ fprintf(stderr, "Unable to sign hash (error=%lu).\n", ERR_get_error());
+
+ return (ret == 1);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : key = clef RSA à utiliser. *
+* hash = empreinte à signer. *
+* sig = signature calculée. *
+* *
+* Description : Vérifie la signature d'une empreinte MD5 avec une clef RSA. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool verify_md5_hash(RSA *key, const unsigned char *hash, unsigned char *sig)
+{
+ int ret; /* Bilan de l'opération */
+
+ ret = RSA_verify(NID_md5, hash, 16, sig, RSA_USED_SIZE, key);
+
+ return (ret == 1);
+
+}
diff --git a/src/analysis/db/keymgn.h b/src/analysis/db/keymgn.h
index 4aa33db..be6bf5e 100644
--- a/src/analysis/db/keymgn.h
+++ b/src/analysis/db/keymgn.h
@@ -26,12 +26,25 @@
#include <stdbool.h>
+#include <openssl/rsa.h>
+/* Taille des clefs RSA */
+#define RSA_USED_SIZE (2048 / 8)
+
/* S'assure que l'utilisateur dispose de clefs RSA. */
bool ensure_user_has_rsa_keys(void);
+/* Charge une clef RSA à partir d'un fichier PEM. */
+RSA *load_rsa_key(const char *, bool);
+
+/* Signe une empreinte MD5 à l'aide d'une clef RSA. */
+bool sign_md5_hash(RSA *, const unsigned char *, unsigned char *);
+
+/* Vérifie la signature d'une empreinte MD5 avec une clef RSA. */
+bool verify_md5_hash(RSA *, const unsigned char *, unsigned char *);
+
#endif /* _ANALYSIS_DB_KEYMGN_H */
diff --git a/src/analysis/db/misc/rlestr.h b/src/analysis/db/misc/rlestr.h
index 7faafd0..2c9387a 100644
--- a/src/analysis/db/misc/rlestr.h
+++ b/src/analysis/db/misc/rlestr.h
@@ -52,6 +52,8 @@ void init_rle_string(rle_string *, const char *);
#define get_rle_string(rle) (rle)->data
+#define get_rle_length(rle) (rle)->length
+
#define is_rle_string_empty(rle) ((rle)->data == NULL)
/* Constitue une représentation de chaîne de caractères. */
diff --git a/src/analysis/db/server.c b/src/analysis/db/server.c
index 3107231..360b266 100644
--- a/src/analysis/db/server.c
+++ b/src/analysis/db/server.c
@@ -35,6 +35,7 @@
#include "cdb.h"
+#include "keymgn.h"
#include "protocol.h"
#include "misc/rlestr.h"
#include "../../common/io.h"
@@ -43,6 +44,14 @@
+/* Utilisateur enregistré */
+typedef struct _registered_user
+{
+ rle_string author; /* Désignation d'utilisateur */
+ char *key_file; /* Chemin vers sa clef publique*/
+
+} registered_user;
+
/* Informations relatives à un client */
typedef struct _cdb_client
{
@@ -60,6 +69,9 @@ struct _GDbServer
{
GObject parent; /* A laisser en premier */
+ registered_user *users; /* Liste d'utilisateurs */
+ size_t users_count; /* Nombre d'enregistrés */
+
int fd; /* Canal de communication */
char *hostname; /* Désignation humaine */
char *desc; /* Désignation du serveur */
@@ -91,12 +103,18 @@ static void g_db_server_init(GDbServer *);
/* Procède à la libération totale de la mémoire. */
static void g_db_server_finalize(GDbServer *);
+/* Supprime toute trace d'utilisateur inscrit. */
+static void g_db_server_unregister_all_user(GDbServer *);
+
+/* Inscrit un nouvel utilisateur comme étant enregistré. */
+static bool g_db_server_register_user(GDbServer *, const char *, char *);
+
+/* Inscrit un nouvel utilisateur comme étant enregistré. */
+static bool g_db_server_is_registered_user(GDbServer *, const rle_string *, unsigned char *);
+
/* Assure l'accueil des nouveaux clients. */
static void *g_db_server_listener(GDbServer *);
-/* Assure le traitement des requêtes de clients. */
-//static void *g_db_server_process(cdb_client **);
-
/* Indique le type défini pour une description de serveur à l'écoute. */
@@ -161,6 +179,8 @@ static void g_db_server_init(GDbServer *server)
static void g_db_server_finalize(GDbServer *server)
{
+ g_db_server_unregister_all_user(server);
+
free(server->hostname);
G_OBJECT_CLASS(g_db_server_parent_class)->finalize(G_OBJECT(server));
@@ -170,27 +190,30 @@ static void g_db_server_finalize(GDbServer *server)
/******************************************************************************
* *
-* Paramètres : host = hôte à représenter pour le service. *
+* Paramètres : author = utilisateur à représenter via le client. *
+* kfile = clef menant à sa clef publique. *
+* host = hôte à représenter pour le service. *
* port = port de connexion pour les clients. *
* *
* Description : Prépare un serveur de BD pour les clients. *
* *
-* Retour : Structure mise en plae ou NULL en cas d'échec. *
+* Retour : Structure mise en place ou NULL en cas d'échec. *
* *
* Remarques : - *
* *
******************************************************************************/
-GDbServer *g_db_server_new(const char *host, short port)
+GDbServer *g_db_server_new(const char *author, char *kfile, const char *host, short port)
{
GDbServer *result; /* Adresse à retourner */
struct hostent *hp; /* Informations sur l'hôte */
size_t desclen; /* Taille de désignation */
-
const char *ip; /* Adresse IPv4 ou IPv6 */
result = g_object_new(G_TYPE_DB_SERVER, NULL);
+ /* ... =*/g_db_server_register_user(result, author, kfile);
+
result->hostname = strdup(host);
hp = gethostbyname(host);
@@ -232,6 +255,121 @@ GDbServer *g_db_server_new(const char *host, short port)
* *
* Paramètres : server = serveur pour les accès distants à manipuler. *
* *
+* Description : Supprime toute trace d'utilisateur inscrit. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_db_server_unregister_all_user(GDbServer *server)
+{
+ size_t i; /* Boucle de parcours */
+
+ for (i = 0; i < server->users_count; i++)
+ {
+ exit_rle_string(&server->users[i].author);
+ free(server->users[i].key_file);
+ }
+
+ if (server->users != NULL)
+ free(server->users);
+
+ server->users = NULL;
+ server->users_count = 0;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : server = serveur pour les accès distants à manipuler. *
+* author = utilisateur à représenter via le client. *
+* kfile = clef menant à sa clef publique. *
+* *
+* Description : Inscrit un nouvel utilisateur comme étant enregistré. *
+* *
+* Retour : Bilan de l'inscription. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static bool g_db_server_register_user(GDbServer *server, const char *author, char *kfile)
+{
+ if (strlen(author) == 0)
+ return false;
+
+ server->users = (registered_user *)realloc(server->users,
+ ++server->users_count * sizeof(registered_user));
+
+ init_rle_string(&server->users[server->users_count - 1].author, author);
+ server->users[server->users_count - 1].key_file = kfile;
+
+ return true;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : server = serveur pour les accès distants à manipuler. *
+* author = utilisateur à représenter via le client. *
+* sig = signature d'utilisateur à valider. *
+* *
+* Description : Inscrit un nouvel utilisateur comme étant enregistré. *
+* *
+* Retour : true si l'utilisateur est bien enregistré, false sinon. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static bool g_db_server_is_registered_user(GDbServer *server, const rle_string *author, unsigned char *sig)
+{
+ bool result; /* Bilan à retourner */
+ size_t i; /* Boucle de parcours */
+ GChecksum *checksum; /* Empreinte MD5 à signer */
+ unsigned char md5_digest[16]; /* Empreinte MD5 calculée */
+ RSA *key; /* Clef pour la signature */
+
+ result = false;
+
+ /* Recherche de l'utilisateur */
+
+ for (i = 0; i < server->users_count; i++)
+ if (cmp_rle_string(&server->users[i].author, author) == 0)
+ break;
+
+ if (i == server->users_count)
+ goto gdsiru_exit;
+
+ /* Validation de la signature présentée */
+
+ checksum = g_checksum_new(G_CHECKSUM_MD5);
+ g_checksum_update(checksum, (guchar *)get_rle_string(author), get_rle_length(author));
+ g_checksum_get_digest(checksum, (guint8 *)md5_digest, (gsize []) { sizeof(md5_digest) });
+ g_checksum_free(checksum);
+
+ key = load_rsa_key(server->users[i].key_file, false);
+ if (key == NULL) goto gdsiru_exit;
+
+ result = verify_md5_hash(key, md5_digest, sig);
+
+ RSA_free(key);
+
+ gdsiru_exit:
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : server = serveur pour les accès distants à manipuler. *
+* *
* Description : Assure l'accueil des nouveaux clients. *
* *
* Retour : NULL. *
@@ -254,6 +392,7 @@ static void *g_db_server_listener(GDbServer *server)
uint32_t version; /* Version du client lue */
rle_string hash; /* Empreinte du binaire visé */
rle_string user; /* Nom d'utilisateur du client */
+ unsigned char sig[RSA_USED_SIZE]; /* Signature effectuée */
GList *iter; /* Boucle de parcours */
fds.fd = server->fd;
@@ -293,6 +432,7 @@ static void *g_db_server_listener(GDbServer *server)
* - le numéro de version du client.
* - l'empreinte du binaire analysé.
* - l'identifiant de l'utilisateur effectuant des modifications.
+ * - la signature de l'empreinte MD5 de l'identifiant.
*
* Tout ceci est à synchroniser avec la fonction g_db_client_start().
*/
@@ -329,6 +469,14 @@ static void *g_db_server_listener(GDbServer *server)
goto gdsl_error_sending;
}
+ if (!safe_recv(fd, sig, RSA_USED_SIZE, 0))
+ {
+ log_variadic_message(LMT_ERROR, _("Error while getting the signature from '%s:%hu'..."),
+ source, ntohs(peer.sin_port));
+ error = DBE_BAD_EXCHANGE;
+ goto gdsl_error_sending;
+ }
+
if (be32toh(cmd) != DBC_HELO)
{
log_variadic_message(LMT_ERROR, _("The client from '%s:%hu' did not introduce itself!"),
@@ -361,6 +509,13 @@ static void *g_db_server_listener(GDbServer *server)
goto gdsl_error_sending;
}
+ if (!g_db_server_is_registered_user(server, &user, sig))
+ {
+ log_variadic_message(LMT_ERROR, _("This user is not registered."));
+ error = DBE_BAD_EXCHANGE;
+ goto gdsl_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.
diff --git a/src/analysis/db/server.h b/src/analysis/db/server.h
index 799ed06..a06e89f 100644
--- a/src/analysis/db/server.h
+++ b/src/analysis/db/server.h
@@ -48,7 +48,7 @@ typedef struct _GDbServerClass GDbServerClass;
GType g_db_server_get_type(void);
/* Prépare un serveur de BD pour les clients. */
-GDbServer *g_db_server_new(const char *, short);
+GDbServer *g_db_server_new(const char *, char *, const char *, short);
/* Démarre le serveur de base de données. */
bool g_db_server_start(GDbServer *);