summaryrefslogtreecommitdiff
path: root/src/analysis/db/server.c
diff options
context:
space:
mode:
authorCyrille Bagard <nocbos@gmail.com>2019-08-29 21:43:47 (GMT)
committerCyrille Bagard <nocbos@gmail.com>2019-08-29 21:43:47 (GMT)
commitfa40856e942a7e1bd1cb2729645182c1fa717468 (patch)
tree954db169d2b734e661d904e502cd1c803f51c6ea /src/analysis/db/server.c
parent7f973e015eb59b626edc584a19a1ad3ffddf4867 (diff)
Defined a new way to launch updates share servers.
Diffstat (limited to 'src/analysis/db/server.c')
-rw-r--r--src/analysis/db/server.c810
1 files changed, 511 insertions, 299 deletions
diff --git a/src/analysis/db/server.c b/src/analysis/db/server.c
index deb54e9..bd221d7 100644
--- a/src/analysis/db/server.c
+++ b/src/analysis/db/server.c
@@ -25,6 +25,8 @@
#include <assert.h>
+#include <dirent.h>
+#include <fcntl.h>
#include <malloc.h>
#include <netdb.h>
#include <poll.h>
@@ -33,55 +35,50 @@
#include <arpa/inet.h>
#include <openssl/err.h>
#include <openssl/ssl.h>
+#include <sys/file.h>
#include <i18n.h>
+#include "auth.h"
#include "cdb.h"
-#include "keymgn.h"
#include "protocol.h"
#include "misc/rlestr.h"
+#include "../../common/extstr.h"
#include "../../common/io.h"
#include "../../common/pathname.h"
#include "../../common/xdg.h"
#include "../../core/logs.h"
-#include "../../core/params.h"
-/* Utilisateur enregistré */
-typedef struct _registered_user
-{
- rle_string author; /* Désignation d'utilisateur */
- char *key_file; /* Chemin vers sa clef publique*/
-
-} registered_user;
-
/* Format générique des adresses de connexion */
typedef union _gen_sockaddr_t
{
- struct sockaddr_in inet_addr; /* Adresse d'écoute IPv4 */
struct sockaddr_un unix_addr; /* Adresse d'écoute Unix */
+ struct sockaddr_in inet4_addr; /* Adresse d'écoute IPv4 */
+ struct sockaddr_in6 inet6_addr; /* Adresse d'écoute IPv6 */
+ struct sockaddr inet_4_6_addr; /* Adresse d'écoute IPv4/6 */
} gen_sockaddr_t;
/* Assure que le point de connexion est vierge. */
-typedef bool (* clean_server_socket_cb) (GDbServer *);
+typedef bool (* lock_server_socket_cb) (GHubServer *);
+
+/* Assure que le point de connexion est rendu disponible. */
+typedef void (* unlock_server_socket_cb) (GHubServer *);
/* Description de serveur à l'écoute (instance) */
-struct _GDbServer
+struct _GHubServer
{
GObject parent; /* A laisser en premier */
- registered_user *users; /* Liste d'utilisateurs */
- size_t users_count; /* Nombre d'enregistrés */
+ char *working; /* Répertoire de travail */
SSL_CTX *tls_ctx; /* Contexte du chiffrement */
- char *cert_file; /* Fichier du certificat */
- char *key_file; /* Fichier de la clef privée */
int fd; /* Canal de communication */
int domain; /* Domaine du canal */
@@ -89,56 +86,60 @@ struct _GDbServer
socklen_t sock_len; /* Taille de cette adresse */
char *desc; /* Désignation du serveur */
- clean_server_socket_cb clean_socket; /* Procédure de nettoyage ? */
-
- char *basedir; /* Répertoire de stockage */
+ lock_server_socket_cb lock_socket; /* Procédure de nettoyage ? */
+ unlock_server_socket_cb unlock_socket; /* Procédure de nettoyage ? */
+ int lock_fd; /* Eventuel verrou d'accès */
GThread *listener; /* Procédure de traitement */
GList *archives; /* Liste des binaires ouverts */
GMutex mutex; /* Verrou pour l'accès */
+ GMutex wait_mutex; /* Accès à la condition */
+ GCond wait_cond; /* Attente de signal */
+
};
/* Description de serveur à l'écoute (classe) */
-struct _GDbServerClass
+struct _GHubServerClass
{
GObjectClass parent; /* A laisser en premier */
};
+/* Indice pour le passage d'arguments */
+static int _ssl_data_index = -1;
+
+
/* Initialise la classe des descriptions de fichier binaire. */
-static void g_db_server_class_init(GDbServerClass *);
+static void g_hub_server_class_init(GHubServerClass *);
/* Initialise une description de fichier binaire. */
-static void g_db_server_init(GDbServer *);
+static void g_hub_server_init(GHubServer *);
/* Supprime toutes les références externes. */
-static void g_db_server_dispose(GDbServer *);
+static void g_hub_server_dispose(GHubServer *);
/* Procède à la libération totale de la mémoire. */
-static void g_db_server_finalize(GDbServer *);
+static void g_hub_server_finalize(GHubServer *);
/* Assure que le point de connexion est vierge. */
-static bool g_db_server_clean_internal_socket(GDbServer *);
+static bool g_hub_server_lock_internal_socket(GHubServer *);
-/* Supprime toute trace d'utilisateur inscrit. */
-static void g_db_server_unregister_all_user(GDbServer *);
+/* Assure que le point de connexion est rendu disponible. */
+static void g_hub_server_unlock_internal_socket(GHubServer *);
-/* 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 *);
+/* Vérifie la légitimité d'une connexion sécurisée au serveur. */
+static int g_hub_server_verify(int, X509_STORE_CTX *);
/* Assure l'accueil des nouveaux clients. */
-static void *g_db_server_listener(GDbServer *);
+static void *g_hub_server_listener(GHubServer *);
/* Indique le type défini pour une description de serveur à l'écoute. */
-G_DEFINE_TYPE(GDbServer, g_db_server, G_TYPE_OBJECT);
+G_DEFINE_TYPE(GHubServer, g_hub_server, G_TYPE_OBJECT);
/******************************************************************************
@@ -153,14 +154,14 @@ G_DEFINE_TYPE(GDbServer, g_db_server, G_TYPE_OBJECT);
* *
******************************************************************************/
-static void g_db_server_class_init(GDbServerClass *klass)
+static void g_hub_server_class_init(GHubServerClass *klass)
{
GObjectClass *object; /* Autre version de la classe */
object = G_OBJECT_CLASS(klass);
- object->dispose = (GObjectFinalizeFunc/* ! */)g_db_server_dispose;
- object->finalize = (GObjectFinalizeFunc)g_db_server_finalize;
+ object->dispose = (GObjectFinalizeFunc/* ! */)g_hub_server_dispose;
+ object->finalize = (GObjectFinalizeFunc)g_hub_server_finalize;
}
@@ -177,18 +178,23 @@ static void g_db_server_class_init(GDbServerClass *klass)
* *
******************************************************************************/
-static void g_db_server_init(GDbServer *server)
+static void g_hub_server_init(GHubServer *server)
{
+ server->working = NULL;
+
server->tls_ctx = NULL;
- server->cert_file = NULL;
- server->key_file = NULL;
server->fd = -1;
- server->basedir = NULL;
+ server->lock_socket = NULL;
+ server->unlock_socket = NULL;
+ server->lock_fd = -1;
g_mutex_init(&server->mutex);
+ g_mutex_init(&server->wait_mutex);
+ g_cond_init(&server->wait_cond);
+
}
@@ -204,11 +210,11 @@ static void g_db_server_init(GDbServer *server)
* *
******************************************************************************/
-static void g_db_server_dispose(GDbServer *server)
+static void g_hub_server_dispose(GHubServer *server)
{
GList *iter; /* Boucle de parcours */
- g_db_server_stop(server);
+ g_hub_server_stop(server);
for (iter = g_list_first(server->archives);
iter != NULL;
@@ -220,7 +226,10 @@ static void g_db_server_dispose(GDbServer *server)
g_mutex_clear(&server->mutex);
- G_OBJECT_CLASS(g_db_server_parent_class)->dispose(G_OBJECT(server));
+ g_mutex_clear(&server->wait_mutex);
+ g_cond_clear(&server->wait_cond);
+
+ G_OBJECT_CLASS(g_hub_server_parent_class)->dispose(G_OBJECT(server));
}
@@ -237,32 +246,37 @@ static void g_db_server_dispose(GDbServer *server)
* *
******************************************************************************/
-static void g_db_server_finalize(GDbServer *server)
+static void g_hub_server_finalize(GHubServer *server)
{
- g_db_server_unregister_all_user(server);
+ int ret; /* Bilan d'un appel */
- free(server->desc);
+ if (server->working != NULL)
+ free(server->working);
assert(server->tls_ctx == NULL);
- if (server->cert_file != NULL)
- free(server->cert_file);
+ free(server->desc);
- if (server->key_file != NULL)
- free(server->key_file);
+ if (server->lock_fd != -1)
+ {
+ ret = flock(server->lock_fd, LOCK_UN);
+ if (ret == -1)
+ LOG_ERROR_N("flock");
- if (server->basedir != NULL)
- free(server->basedir);
+ ret = close(server->lock_fd);
+ if (ret == -1)
+ LOG_ERROR_N("close");
- G_OBJECT_CLASS(g_db_server_parent_class)->finalize(G_OBJECT(server));
+ }
+
+ G_OBJECT_CLASS(g_hub_server_parent_class)->finalize(G_OBJECT(server));
}
/******************************************************************************
* *
-* Paramètres : author = utilisateur à représenter via le client. *
-* kfile = clef menant à sa clef publique. *
+* Paramètres : - *
* *
* Description : Prépare un serveur de BD pour les clients internes. *
* *
@@ -272,49 +286,21 @@ static void g_db_server_finalize(GDbServer *server)
* *
******************************************************************************/
-GDbServer *g_db_server_new_internal(const char *author, char *kfile)
+GHubServer *g_hub_server_new_internal(void)
{
- GDbServer *result; /* Adresse à retourner */
+ GHubServer *result; /* Adresse à retourner */
bool status; /* Bilan d'un chargement */
- char *working; /* Répertoire par le client */
- int ret[2]; /* Bilan d'une vérification */
-
- result = g_object_new(G_TYPE_DB_SERVER, NULL);
-
- /* Chargement du profil */
-
- status = g_db_server_register_user(result, author, kfile);
- if (!status) goto gdsni_error;
-
- /* Paramètres de chiffrement */
-
- working = get_cert_working_directory("servers", "standalone");
-
- result->cert_file = build_absolute_filename(working, "server-cert.pem");
- result->key_file = build_absolute_filename(working, "server-key.pem");
- ret[0] = access(result->cert_file, R_OK);
- ret[1] = access(result->key_file, R_OK);
+ result = g_object_new(G_TYPE_HUB_SERVER, NULL);
- if (ret[0] != 0 || ret[1] != 0)
- {
- log_variadic_message(LMT_ERROR, _("The serveur certificate is missing in the '%s' directory!"), working);
- log_simple_message(LMT_ERROR, _("Generate it using the menu (Tool > Identity) and restart the program."));
-
- free(working);
-
- goto gdsni_missing_pem;
-
- }
-
- free(working);
+ result->working = get_db_working_directory("servers", "standalone", NULL, NULL);
/* Détermination du point d'écoute */
result->domain = AF_UNIX;
- status = build_tmp_socket("internal-server", &result->addr.unix_addr);
- if (!status) goto gdsni_error;
+ status = build_internal_server_socket(&result->addr.unix_addr);
+ if (!status) goto sock_error;
result->sock_len = sizeof(struct sockaddr_un);
@@ -324,17 +310,12 @@ GDbServer *g_db_server_new_internal(const char *author, char *kfile)
/* Aide pour une sortie propre ? */
- result->clean_socket = g_db_server_clean_internal_socket;
-
- /* Répertoire de stockage */
-
- result->basedir = get_xdg_config_dir("chrysalide" G_DIR_SEPARATOR_S "cdbs");
+ result->lock_socket = g_hub_server_lock_internal_socket;
+ result->unlock_socket = g_hub_server_unlock_internal_socket;
return result;
- gdsni_missing_pem:
-
- gdsni_error:
+ sock_error:
g_object_unref(G_OBJECT(result));
@@ -355,16 +336,42 @@ GDbServer *g_db_server_new_internal(const char *author, char *kfile)
* *
******************************************************************************/
-static bool g_db_server_clean_internal_socket(GDbServer *server)
+static bool g_hub_server_lock_internal_socket(GHubServer *server)
{
bool result; /* Bilan à faire remonter */
char *sock_path; /* Chemin vers le canal UNIX */
+ char *lock_path; /* Chemin vers le verrou */
int ret; /* Bilan d'un appel */
- result = true;
+ result = false;
sock_path = server->addr.unix_addr.sun_path;
+ /* Partie d'exclusivité */
+
+ lock_path = strdup(sock_path);
+ lock_path = stradd(lock_path, ".lock");
+
+ assert(server->lock_fd == -1);
+
+ server->lock_fd = open(lock_path, O_RDONLY | O_CREAT, 0600);
+ if (server->lock_fd == -1)
+ {
+ LOG_ERROR_N("open");
+ goto exit;
+ }
+
+ free(lock_path);
+
+ ret = flock(server->lock_fd, LOCK_EX | LOCK_NB);
+
+ result = (ret == 0);
+
+ if (!result)
+ goto exit;
+
+ /* Partie de nettoye */
+
ret = access(sock_path, F_OK);
if (ret == 0)
@@ -373,12 +380,24 @@ static bool g_db_server_clean_internal_socket(GDbServer *server)
if (ret != 0)
{
- perror("unlink");
+ LOG_ERROR_N("unlink");
+
+ ret = flock(server->lock_fd, LOCK_UN);
+ if (ret == -1)
+ LOG_ERROR_N("flock");
+
+ ret = close(server->lock_fd);
+ if (ret == -1)
+ LOG_ERROR_N("close");
+
result = false;
+
}
}
+ exit:
+
return result;
}
@@ -386,90 +405,164 @@ static bool g_db_server_clean_internal_socket(GDbServer *server)
/******************************************************************************
* *
-* Paramètres : conf = fichier de configuration à interpréter. *
+* Paramètres : server = instance à consulter et nettoyer. *
* *
-* Description : Prépare un serveur de BD pour les clients distants. *
+* Description : Assure que le point de connexion est rendu disponible. *
* *
-* Retour : Structure mise en place ou NULL en cas d'échec. *
+* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
-GDbServer *g_db_server_new_remote(const char *conf)
+static void g_hub_server_unlock_internal_socket(GHubServer *server)
{
- GDbServer *result; /* Adresse à retourner */
+ int ret; /* Bilan d'un appel */
+
+ assert(server->lock_fd != -1);
+
+ ret = flock(server->lock_fd, LOCK_UN);
+ if (ret == -1)
+ LOG_ERROR_N("flock");
- char *host;
- short port;
+ ret = close(server->lock_fd);
+ if (ret == -1)
+ LOG_ERROR_N("close");
+
+ server->lock_fd = -1;
+
+}
- struct hostent *hp; /* Informations sur l'hôte */
+/******************************************************************************
+* *
+* Paramètres : host = désignation du serveur à lancer. *
+* port = port d'écoute à ouvrir. *
+* ipv6 = adopte une préférence pour les adresses IPv6. *
+* *
+* Description : Prépare un serveur de BD pour les clients distants. *
+* *
+* Retour : Structure mise en place ou NULL en cas d'échec. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+GHubServer *g_hub_server_new_remote(const char *host, const char *port, bool ipv6)
+{
+ GHubServer *result; /* Adresse à retourner */
+ struct addrinfo hints; /* Cadre de connexion souhaité */
+ struct addrinfo *available; /* Cadres de connexion dispos */
+ int ret; /* Bilan d'une consultation */
+ struct addrinfo *iter; /* Boucle de parcours */
size_t desclen; /* Taille de désignation */
+ struct sockaddr_in *addr4; /* Adresse IPv4 brute */
+ struct sockaddr_in6 *addr6; /* Adresse IPv6 brute */
const char *ip; /* Adresse IPv4 ou IPv6 */
- const char *tmpdir; /* Répertoire de travail */
- bool status; /* Bilan d'un consultation */
- result = g_object_new(G_TYPE_DB_SERVER, NULL);
+ result = g_object_new(G_TYPE_HUB_SERVER, NULL);
- /* Chargement des profils */
+ assert(host != NULL);
+ if (port == NULL)
+ port = "1337";
- /*
- ret = g_db_server_register_user(result, author, kfile);
- if (!ret) goto gdsni_error;
- */
+ result->working = get_db_working_directory("servers", host, port, NULL);
- result->domain = AF_INET;
+ /* Détermination du point d'écoute */
- host = "localhost";
- port = 9999;
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_protocol = IPPROTO_TCP;
- /* Détermination du point d'écoute */
+ ret = getaddrinfo(host, port, &hints, &available);
+ if (ret != 0)
+ {
+ LOG_ERROR_GAI_N("getaddrinfo", ret);
+ goto error;
+ }
+
+ result->domain = AF_UNSPEC;
- hp = gethostbyname(host);
- if (hp == NULL)
+ /**
+ * Premier tour : on essaie de se plier à la demande.
+ */
+
+ for (iter = available; iter != NULL && result->domain == AF_UNSPEC; iter = iter->ai_next)
{
- perror("gethostbyname");
- goto gdsn_error;
+ if (ipv6 && iter->ai_family != AF_INET6)
+ continue;
+
+ if (!ipv6 && iter->ai_family != AF_INET)
+ continue;
+
+ result->domain = iter->ai_family;
+
+ memcpy(&result->addr.inet_4_6_addr, iter->ai_addr, iter->ai_addrlen);
+ result->sock_len = iter->ai_addrlen;
+
}
- result->addr.inet_addr.sin_family = hp->h_addrtype;
- memcpy(&result->addr.inet_addr.sin_addr, hp->h_addr_list[0], sizeof(struct in_addr));
+ /**
+ * Second tour : on fait avec ce qu'on a.
+ */
- result->addr.inet_addr.sin_port = htons(port);
+ for (iter = available; iter != NULL && result->domain == AF_UNSPEC; iter = iter->ai_next)
+ {
+ if (iter->ai_family != AF_INET6 && iter->ai_family != AF_INET)
+ continue;
- result->sock_len = sizeof(struct sockaddr_in);
+ result->domain = iter->ai_family;
- desclen = INET6_ADDRSTRLEN + 1 + 5 + 1;
- result->desc = calloc(desclen, sizeof(char));
+ memcpy(&result->addr.inet_4_6_addr, iter->ai_addr, iter->ai_addrlen);
+ result->sock_len = iter->ai_addrlen;
- ip = inet_ntop(AF_INET, &result->addr.inet_addr.sin_addr, result->desc, INET6_ADDRSTRLEN);
- if (ip == NULL)
+ }
+
+ if (available != NULL)
+ freeaddrinfo(available);
+
+ if (result->domain == AF_UNSPEC)
{
- perror("inet_ntop");
- goto gdsn_error;
+ log_variadic_message(LMT_ERROR, _("No suitable address found for %s:%s"), host, port);
+ goto error;
}
/* Désignation humaine */
- snprintf(result->desc + strlen(ip), 1 + 5, ":%hu", port);
+ desclen = INET6_ADDRSTRLEN + 1 + strlen(port) + 1;
+ result->desc = calloc(desclen, sizeof(char));
- /* Aide pour une sortie propre ? */
+ if (result->domain == AF_INET)
+ {
+ addr4 = (struct sockaddr_in *)&result->addr.inet_4_6_addr;
+ ip = inet_ntop(result->domain, &addr4->sin_addr, result->desc, INET6_ADDRSTRLEN);
+ }
- result->clean_socket = NULL;
+ else if (result->domain == AF_INET6)
+ {
+ addr6 = (struct sockaddr_in6 *)&result->addr.inet_4_6_addr;
+ ip = inet_ntop(result->domain, &addr6->sin6_addr, result->desc, INET6_ADDRSTRLEN);
+ }
- /* Répertoire de stockage */
+ else
+ assert(false);
- status = g_generic_config_get_value(get_main_configuration(), MPK_TMPDIR, &tmpdir);
- if (!status) goto gdsn_error;
+ if (ip == NULL)
+ {
+ LOG_ERROR_N("inet_ntop");
+ goto error;
+ }
- result->basedir = strdup(tmpdir);
+ log_variadic_message(LMT_INFO, _("Resolved server IP: %s"), ip);
+
+ snprintf(result->desc + strlen(ip), 1 + strlen(port) + 1, ":%s", port);
return result;
- gdsn_error:
+ error:
g_object_unref(G_OBJECT(result));
@@ -480,112 +573,126 @@ GDbServer *g_db_server_new_remote(const char *conf)
/******************************************************************************
* *
-* Paramètres : server = serveur pour les accès distants à manipuler. *
+* Paramètres : preverify_ok = état de la prévalidation. *
+* ctx = contexte de la certification en cours. *
* *
-* Description : Supprime toute trace d'utilisateur inscrit. *
+* Description : Vérifie la légitimité d'une connexion sécurisée au serveur. *
* *
-* Retour : - *
+* Retour : 1 si la connexion est validée, 0 sinon. *
* *
* Remarques : - *
* *
******************************************************************************/
-static void g_db_server_unregister_all_user(GDbServer *server)
+static int g_hub_server_verify(int preverify_ok, X509_STORE_CTX *ctx)
{
- size_t i; /* Boucle de parcours */
+ int result; /* Bilan à retourner */
+ X509 *peer_cert; /* Certificat côté client */
+ SSL *ssl; /* Structure de connexion SLL */
+ SSL_CTX *ssl_ctx; /* Contexte SSL du serveur */
+ GHubServer *server; /* Serveur concerné */
+ char *filename; /* Chemin d'accès reconstruit */
+ FILE *stream; /* Flux ouvert en lecture */
+ X509 *ca_cert; /* Certificat CA du serveur */
+ int status; /* Bilan d'une comparaison */
+ char *authorized_dir; /* Répertoire des autorisations*/
+ DIR *dir; /* Répertoire à parcourir */
+ struct dirent *entry; /* Elément trouvé */
+ X509 *authorized_cert; /* Certificat de client validé */
- for (i = 0; i < server->users_count; i++)
- {
- exit_rle_string(&server->users[i].author);
- free(server->users[i].key_file);
- }
+ result = 0;
- if (server->users != NULL)
- free(server->users);
+ peer_cert = X509_STORE_CTX_get_current_cert(ctx);
- server->users = NULL;
- server->users_count = 0;
+ /* Récupération du serveur interne */
-}
+ ssl = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
+ ssl_ctx = SSL_get_SSL_CTX(ssl);
+ server = SSL_CTX_get_ex_data(ssl_ctx, _ssl_data_index);
+ assert(server != NULL);
-/******************************************************************************
-* *
-* 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 : - *
-* *
-******************************************************************************/
+ /* Test du certificat d'autorité */
-static bool g_db_server_register_user(GDbServer *server, const char *author, char *kfile)
-{
- if (strlen(author) == 0)
- return false;
+ filename = strdup(server->working);
+ filename = stradd(filename, "ca-cert.pem");
- server->users = realloc(server->users, ++server->users_count * sizeof(registered_user));
+ stream = fopen(filename, "rb");
+ if (stream == NULL) goto authorized;
- dup_into_rle_string(&server->users[server->users_count - 1].author, author);
- server->users[server->users_count - 1].key_file = kfile;
+ ca_cert = PEM_read_X509(stream, NULL, NULL, NULL);
- return true;
+ fclose(stream);
-}
+ status = X509_cmp(peer_cert, ca_cert);
+ if (status == 0)
+ result = 1;
-/******************************************************************************
-* *
-* 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 : - *
-* *
-******************************************************************************/
+ X509_free(ca_cert);
-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 */
+ free(filename);
- result = false;
+ if (result == 1)
+ goto verified;
+
+ /* Détermination du répertoire des autorisations */
+
+ authorized:
+
+ authorized_dir = strdup(server->working);
+ authorized_dir = stradd(authorized_dir, "authorized" G_DIR_SEPARATOR_S);
- /* Recherche de l'utilisateur */
+ dir = opendir(authorized_dir);
+ if (dir == NULL) goto verified;
+
+ /* Recherche d'une entrée de validation */
+
+ while (result == 0)
+ {
+ entry = readdir(dir);
+
+ if (entry == NULL)
+ {
+ if (errno != 0)
+ LOG_ERROR_N("readdir");
- 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;
+ }
+
+ if (entry->d_type != DT_REG && entry->d_type != DT_LNK) continue;
+ if (entry->d_name[0] == '.') continue;
+
+ filename = strdup(authorized_dir);
+ filename = stradd(filename, G_DIR_SEPARATOR_S);
+ filename = stradd(filename, entry->d_name);
+
+ stream = fopen(filename, "rb");
+ if (stream == NULL) goto next;
- /* Validation de la signature présentée */
+ authorized_cert = PEM_read_X509(stream, NULL, NULL, NULL);
- 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);
+ fclose(stream);
- key = load_rsa_key(server->users[i].key_file, false);
- if (key == NULL) goto gdsiru_exit;
+ status = X509_cmp(peer_cert, authorized_cert);
- result = verify_md5_hash(key, md5_digest, sig);
+ if (status == 0)
+ result = 1;
- RSA_free(key);
+ X509_free(authorized_cert);
- gdsiru_exit:
+ next:
+
+ free(filename);
+
+ }
+
+ /* Sortie */
+
+ closedir(dir);
+
+ verified:
return result;
@@ -604,7 +711,7 @@ static bool g_db_server_is_registered_user(GDbServer *server, const rle_string *
* *
******************************************************************************/
-static void *g_db_server_listener(GDbServer *server)
+static void *g_hub_server_listener(GHubServer *server)
{
struct pollfd fds; /* Surveillance des flux */
int ret; /* Bilan d'un appel */
@@ -612,29 +719,37 @@ static void *g_db_server_listener(GDbServer *server)
int fd; /* Canal établi vers un client */
SSL *tls_fd; /* Même canal, mais sécurisé */
rle_string hash; /* Empreinte du binaire visé */
- rle_string user; /* Nom d'utilisateur du client */
const char *ip; /* Statut de la conversion */
char *peer_name; /* Désignation du correspondant*/
DBError error; /* Validation de la connexion */
GCdbArchive *archive; /* Destinataire final du client*/
+ GList *iter; /* Boucle de parcours */
packed_buffer in_pbuf; /* Tampon de réception */
bool status; /* Bilan d'une opération */
uint32_t cmd; /* Commande initiale lue */
uint32_t version; /* Version du client lue */
- unsigned char sig[RSA_USED_SIZE]; /* Signature effectuée */
- GList *iter; /* Boucle de parcours */
+ char *basedir; /* Répertoire de stockage */
+ char *tmpdir; /* Répertoire de travail */
packed_buffer out_pbuf; /* Tampon d'émission */
fds.fd = server->fd;
fds.events = POLLIN | POLLPRI;
- while (1)
+ while (server->fd != -1)
{
ret = poll(&fds, 1, -1);
if (ret != 1) continue;
/* Le canal est fermé, une sortie doit être demandée... */
- if (fds.revents & POLLHUP)
+ if (fds.revents & POLLNVAL)
+ break;
+
+ /**
+ * Même chose, cf. "TCP: When is EPOLLHUP generated?"
+ * https://stackoverflow.com/questions/52976152/tcp-when-is-epollhup-generated/52976327#52976327
+ */
+
+ if (fds.revents & (POLLHUP | POLLRDHUP))
break;
if (fds.revents & (POLLIN | POLLPRI))
@@ -642,7 +757,7 @@ static void *g_db_server_listener(GDbServer *server)
fd = accept(server->fd, (struct sockaddr *)&peer, (socklen_t []) { sizeof(gen_sockaddr_t) });
if (fd == -1)
{
- perror("accept");
+ LOG_ERROR_N("accept");
continue;
}
@@ -667,7 +782,6 @@ static void *g_db_server_listener(GDbServer *server)
/* Initialisation à vide pour les sorties en erreur */
init_dynamic_rle_string(&hash, NULL);
- init_dynamic_rle_string(&user, NULL);
/* Construction d'une représentation */
@@ -678,14 +792,29 @@ static void *g_db_server_listener(GDbServer *server)
{
peer_name = calloc(INET6_ADDRSTRLEN + 1 + 5 + 1, sizeof(char));
- ip = inet_ntop(AF_INET, &peer.inet_addr.sin_addr, peer_name, INET6_ADDRSTRLEN);
+ ip = inet_ntop(AF_INET, &peer.inet4_addr.sin_addr, peer_name, INET6_ADDRSTRLEN);
+ if (ip == NULL)
+ {
+ LOG_ERROR_N("inet_ntop");
+ goto id_error;
+ }
+
+ snprintf(peer_name + strlen(ip), 1 + 5, ":%hu", ntohs(peer.inet4_addr.sin_port));
+
+ }
+
+ else if (*((sa_family_t *)&peer) == AF_INET6)
+ {
+ peer_name = calloc(INET6_ADDRSTRLEN + 1 + 5 + 1, sizeof(char));
+
+ ip = inet_ntop(AF_INET6, &peer.inet6_addr.sin6_addr, peer_name, INET6_ADDRSTRLEN);
if (ip == NULL)
{
- perror("inet_ntop");
+ LOG_ERROR_N("inet_ntop");
goto id_error;
}
- snprintf(peer_name + strlen(ip), 1 + 5, ":%hu", ntohs(peer.inet_addr.sin_port));
+ snprintf(peer_name + strlen(ip), 1 + 5, ":%hu", ntohs(peer.inet6_addr.sin6_port));
}
@@ -695,13 +824,13 @@ static void *g_db_server_listener(GDbServer *server)
error = DBE_NONE;
archive = NULL;
+ iter = NULL;
+
/**
* Le premier "paquet" reçu de la part d'un client doit contenir les informations suivantes :
* - la commande 'DBC_HELO'.
* - le numéro de version du client.
* - 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().
*/
@@ -744,24 +873,6 @@ static void *g_db_server_listener(GDbServer *server)
goto error_sending;
}
- status = unpack_rle_string(&user, &in_pbuf);
- if (!status)
- {
- log_variadic_message(LMT_ERROR, _("Error while getting the user name from '%s'..."),
- peer_name);
- error = DBE_BAD_EXCHANGE;
- goto error_sending;
- }
-
- status = extract_packed_buffer(&in_pbuf, sig, RSA_USED_SIZE, false);
- if (!status)
- {
- log_variadic_message(LMT_ERROR, _("Error while getting the signature from '%s'..."),
- peer_name);
- error = DBE_BAD_EXCHANGE;
- goto error_sending;
- }
-
if (cmd != DBC_HELO)
{
log_variadic_message(LMT_ERROR, _("The client from '%s' did not introduce itself!"),
@@ -786,21 +897,6 @@ static void *g_db_server_listener(GDbServer *server)
goto error_sending;
}
- if (is_rle_string_empty(&user))
- {
- log_variadic_message(LMT_ERROR, _("No user is associated with the client from '%s'..."),
- peer_name);
- error = DBE_BAD_EXCHANGE;
- goto 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 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.
@@ -818,7 +914,19 @@ static void *g_db_server_listener(GDbServer *server)
}
if (iter == NULL)
- archive = g_cdb_archive_new(server->basedir, &hash, &user, &error);
+ {
+ basedir = strdup(server->working);
+ basedir = stradd(basedir, "cdbs" G_DIR_SEPARATOR_S);
+
+ tmpdir = strdup(server->working);
+ tmpdir = stradd(tmpdir, "tmp" G_DIR_SEPARATOR_S);
+
+ archive = g_cdb_archive_new(basedir, tmpdir, &hash, &error);
+
+ free(tmpdir);
+ free(basedir);
+
+ }
/**
* Le serveur doit répondre pour un message type :
@@ -856,14 +964,13 @@ static void *g_db_server_listener(GDbServer *server)
if (iter == NULL)
server->archives = g_list_append(server->archives, archive);
- g_cdb_archive_add_client(archive, tls_fd, &user);
+ g_cdb_archive_add_client(archive, tls_fd);
exit_packed_buffer(&out_pbuf);
free(peer_name);
exit_rle_string(&hash);
- exit_rle_string(&user);
continue;
@@ -884,7 +991,6 @@ static void *g_db_server_listener(GDbServer *server)
free(peer_name);
exit_rle_string(&hash);
- exit_rle_string(&user);
invalid_conn:
@@ -898,6 +1004,8 @@ static void *g_db_server_listener(GDbServer *server)
}
+ g_hub_server_stop(server);
+
return NULL;
}
@@ -905,7 +1013,9 @@ static void *g_db_server_listener(GDbServer *server)
/******************************************************************************
* *
-* Paramètres : server = serveur pour les accès distants à manipuler. *
+* Paramètres : server = serveur pour les accès distants à manipuler. *
+* backlog = nombre de connexions maximal. *
+* keep = conservation du serveur en avant plan. *
* *
* Description : Démarre le serveur de base de données. *
* *
@@ -915,12 +1025,16 @@ static void *g_db_server_listener(GDbServer *server)
* *
******************************************************************************/
-bool g_db_server_start(GDbServer *server)
+ServerStartStatus g_hub_server_start(GHubServer *server, int backlog, bool keep)
{
+ ServerStartStatus result; /* Bilan à retourner */
const SSL_METHOD *method; /* Mode du canal sécurisé */
+ char *filename; /* Fichier PEM à manipuler */
int ret; /* Bilan d'un appel */
+ STACK_OF(X509_NAME) *ca_cert; /* Certificat de l'autorité */
bool status; /* Bilan d'un nettoyage */
- int backlog; /* Nombre de connexions maximal*/
+
+ result = SSS_FAILURE;
/* Définition d'un environnement TLS */
@@ -934,7 +1048,12 @@ bool g_db_server_start(GDbServer *server)
goto quick_error;
}
- ret = SSL_CTX_use_certificate_file(server->tls_ctx, server->cert_file, SSL_FILETYPE_PEM);
+ filename = strdup(server->working);
+ filename = stradd(filename, "server-cert.pem");
+
+ ret = SSL_CTX_use_certificate_file(server->tls_ctx, filename, SSL_FILETYPE_PEM);
+
+ free(filename);
if (ret != 1)
{
@@ -942,7 +1061,12 @@ bool g_db_server_start(GDbServer *server)
goto tls_error;
}
- ret = SSL_CTX_use_PrivateKey_file(server->tls_ctx, server->key_file, SSL_FILETYPE_PEM);
+ filename = strdup(server->working);
+ filename = stradd(filename, "server-key.pem");
+
+ ret = SSL_CTX_use_PrivateKey_file(server->tls_ctx, filename, SSL_FILETYPE_PEM);
+
+ free(filename);
if (ret != 1)
{
@@ -958,52 +1082,99 @@ bool g_db_server_start(GDbServer *server)
goto tls_error;
}
+ /* Validation des certificats */
+
+ if (_ssl_data_index == -1)
+ {
+ _ssl_data_index = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL);
+ assert(_ssl_data_index != -1);
+ }
+
+ ret = SSL_CTX_set_ex_data(server->tls_ctx, _ssl_data_index, server);
+ if (ret != 1)
+ {
+ LOG_ERROR_OPENSSL;
+ goto tls_error;
+ }
+
+ SSL_CTX_set_verify(server->tls_ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, g_hub_server_verify);
+
+ filename = strdup(server->working);
+ filename = stradd(filename, "ca-cert.pem");
+
+ ca_cert = SSL_load_client_CA_file(filename);
+
+ free(filename);
+
+ if (ca_cert == NULL)
+ {
+ LOG_ERROR_OPENSSL;
+ goto tls_error;
+ }
+
+ SSL_CTX_set_client_CA_list(server->tls_ctx, ca_cert);
+
/* Mise en place d'un canal de communication */
server->fd = socket(server->domain, SOCK_STREAM, 0);
if (server->fd == -1)
{
- perror("socket");
+ LOG_ERROR_N("socket");
return false;
}
ret = setsockopt(server->fd, SOL_SOCKET, SO_REUSEADDR, (int []) { 1 }, sizeof(int));
if (ret == -1)
{
- perror("setsockopt");
- goto gdss_error;
+ LOG_ERROR_N("setsockopt");
+ goto network_error;
}
- if (server->clean_socket != NULL)
+ if (server->lock_socket != NULL)
{
- status = server->clean_socket(server);
- if (!status) goto gdss_error;
+ status = server->lock_socket(server);
+ if (!status)
+ {
+ result = SSS_ALREADY_RUNNING;
+ goto network_error;
+ }
+
}
ret = bind(server->fd, (struct sockaddr *)&server->addr, server->sock_len);
if (ret == -1)
{
- perror("bind");
- goto gdss_error;
+ LOG_ERROR_N("bind");
+ goto network_error;
}
- if (!g_generic_config_get_value(get_main_configuration(), MPK_SERVER_BACKLOG, &backlog))
- goto gdss_error;
-
ret = listen(server->fd, backlog);
if (ret == -1)
{
- perror("listen");
- goto gdss_error;
+ LOG_ERROR_N("listen");
+ goto network_error;
}
- server->listener = g_thread_new("cdb_listener", (GThreadFunc)g_db_server_listener, server);
+ if (!keep)
+ {
+ ret = daemon(1, 1);
+ if (ret != 0)
+ {
+ LOG_ERROR_N("daemon");
+ goto network_error;
+ }
+
+ }
+
+ server->listener = g_thread_new("cdb_listener", (GThreadFunc)g_hub_server_listener, server);
log_variadic_message(LMT_PROCESS, _("Server started and listening at %s"), server->desc);
- return true;
+ result = SSS_SUCCESS;
- gdss_error:
+ return result;
+
+ network_error:
close(server->fd);
server->fd = -1;
@@ -1015,7 +1186,30 @@ bool g_db_server_start(GDbServer *server)
quick_error:
- return false;
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : server = serveur pour les accès distants à consulter. *
+* *
+* Description : Attend l'arrête du serveur de base de données. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+void g_hub_server_wait_for_stop(GHubServer *server)
+{
+ g_mutex_lock(&server->wait_mutex);
+
+ g_cond_wait(&server->wait_cond, &server->wait_mutex);
+
+ g_mutex_unlock(&server->wait_mutex);
}
@@ -1032,34 +1226,52 @@ bool g_db_server_start(GDbServer *server)
* *
******************************************************************************/
-void g_db_server_stop(GDbServer *server)
+void g_hub_server_stop(GHubServer *server)
{
+ int fd; /* Canal à clôturer */
int ret; /* Bilan d'un appel */
/* Canal de communication */
if (server->fd == -1)
{
- assert(server->tls_ctx == NULL);
+ /**
+ * Si la fermture est forcée, le thread de traitement va terminer en erreur.
+ * Donc cette fonction sera appelée deux fois. Seule la première va affecter
+ * le contexte, donc on le peut pas s'assurer de la condition suivante dans
+ * tous les cas.
+ */
+
+ /*assert(client->tls_ctx == NULL);*/
return;
}
- ret = shutdown(server->fd, SHUT_RDWR);
- if (ret == -1) perror("shutdown");
+ fd = server->fd;
- g_thread_join(server->listener);
+ server->fd = -1;
- ret = close(server->fd);
- if (ret == -1) perror("close");
+ ret = shutdown(fd, SHUT_RDWR);
+ if (ret == -1) LOG_ERROR_N("shutdown");
- server->fd = -1;
+ ret = close(fd);
+ if (ret == -1) LOG_ERROR_N("close");
+
+ g_thread_join(server->listener);
+
+ /* Verrou d'accès */
- if (server->clean_socket != NULL)
- server->clean_socket(server);
+ if (server->unlock_socket != NULL)
+ server->unlock_socket(server);
/* Environnement TLS */
SSL_CTX_free(server->tls_ctx);
server->tls_ctx = NULL;
+ /* Fin de service */
+
+ g_mutex_lock(&server->wait_mutex);
+ g_cond_signal(&server->wait_cond);
+ g_mutex_unlock(&server->wait_mutex);
+
}