diff options
Diffstat (limited to 'src/analysis')
-rw-r--r-- | src/analysis/binary.c | 34 | ||||
-rw-r--r-- | src/analysis/binary.h | 2 | ||||
-rw-r--r-- | src/analysis/contents/file.c | 2 | ||||
-rw-r--r-- | src/analysis/db/Makefile.am | 1 | ||||
-rw-r--r-- | src/analysis/db/auth.c | 705 | ||||
-rw-r--r-- | src/analysis/db/auth.h | 59 | ||||
-rw-r--r-- | src/analysis/db/cdb.c | 84 | ||||
-rw-r--r-- | src/analysis/db/cdb.h | 4 | ||||
-rw-r--r-- | src/analysis/db/certs.c | 4 | ||||
-rw-r--r-- | src/analysis/db/certs.h | 4 | ||||
-rw-r--r-- | src/analysis/db/client.c | 373 | ||||
-rw-r--r-- | src/analysis/db/client.h | 34 | ||||
-rw-r--r-- | src/analysis/db/keymgn.c | 6 | ||||
-rw-r--r-- | src/analysis/db/keymgn.h | 2 | ||||
-rw-r--r-- | src/analysis/db/protocol.h | 2 | ||||
-rw-r--r-- | src/analysis/db/server.c | 810 | ||||
-rw-r--r-- | src/analysis/db/server.h | 38 |
17 files changed, 1640 insertions, 524 deletions
diff --git a/src/analysis/binary.c b/src/analysis/binary.c index 80d12d3..5ac8e04 100644 --- a/src/analysis/binary.c +++ b/src/analysis/binary.c @@ -72,8 +72,8 @@ struct _GLoadedBinary char *remote_host; /* Nom du serveur distant */ unsigned short remote_port; /* Port du serveur distant */ - GDbClient *local; /* Enregistrements locaux */ - GDbClient *remote; /* Enregistrements distants */ + GHubClient *local; /* Enregistrements locaux */ + GHubClient *remote; /* Enregistrements distants */ DBStorage storages[DBF_COUNT]; /* Lieux d'enregistrement */ GList *collections; /* Ensemble de modifications */ @@ -862,12 +862,12 @@ static bool g_loaded_binary_connect_internal(GLoadedBinary *binary) /* Tentative de connexion */ - binary->local = g_db_client_new(author, priv, + binary->local = g_hub_client_new(/*author, priv, g_loaded_binary_get_name(binary, false), - checksum, + */checksum, binary->collections); - result = g_db_client_start_internal(binary->local); + result = g_hub_client_start_internal(binary->local); glbcl_exit: @@ -920,12 +920,12 @@ static bool g_loaded_binary_connect_remote(GLoadedBinary *binary) /* Tentative de connexion */ - binary->remote = g_db_client_new(author, priv, + binary->remote = g_hub_client_new(/*author, priv, g_loaded_binary_get_name(binary, false), - checksum, + */checksum, binary->collections); - result = g_db_client_start_remote(binary->local, binary->remote_host, binary->remote_port); + result = g_hub_client_start_remote(binary->local, binary->remote_host, "1337", true);//binary->remote_port); if (!result) { @@ -1008,9 +1008,9 @@ bool g_loaded_binary_save_cache(const GLoadedBinary *binary) * * ******************************************************************************/ -GDbClient *g_loaded_binary_get_db_client(const GLoadedBinary *binary) +GHubClient *g_loaded_binary_get_db_client(const GLoadedBinary *binary) { - GDbClient *result; /* Instance à retourner */ + GHubClient *result; /* Instance à retourner */ result = binary->local; @@ -1106,7 +1106,7 @@ bool _g_loaded_binary_add_to_collection(GLoadedBinary *binary, GDbItem *item, bo DBFeatures feature; /* Domaine de fonctionnalité */ GDbCollection *collec; /* Collection visée au final */ DBStorage storage; /* Forme d'enregistrement */ - GDbClient *client; /* Liaison à utiliser */ + GHubClient *client; /* Liaison à utiliser */ packed_buffer out_pbuf; /* Tampon d'émission */ int fd; /* Identifiant du canal de com.*/ @@ -1134,11 +1134,11 @@ bool _g_loaded_binary_add_to_collection(GLoadedBinary *binary, GDbItem *item, bo init_packed_buffer(&out_pbuf); - fd = g_db_client_get_fd(client); + fd = g_hub_client_get_fd(client); result = g_db_collection_pack(collec, &out_pbuf, DBA_ADD_ITEM, item); - g_db_client_put_fd(client); + g_hub_client_put_fd(client); if (result) result = send_packed_buffer(&out_pbuf, fd); @@ -1175,7 +1175,7 @@ bool _g_loaded_binary_remove_from_collection(GLoadedBinary *binary, DBFeatures f bool result; /* Bilan à faire remonter */ GDbCollection *collec; /* Collection visée au final */ DBStorage storage; /* Forme d'enregistrement */ - GDbClient *client; /* Liaison à utiliser */ + GHubClient *client; /* Liaison à utiliser */ packed_buffer out_pbuf; /* Tampon d'émission */ int fd; /* Identifiant du canal de com.*/ @@ -1201,11 +1201,11 @@ bool _g_loaded_binary_remove_from_collection(GLoadedBinary *binary, DBFeatures f init_packed_buffer(&out_pbuf); - fd = g_db_client_get_fd(client); + fd = g_hub_client_get_fd(client); result = g_db_collection_pack(collec, &out_pbuf, DBA_REM_ITEM, item); - g_db_client_put_fd(client); + g_hub_client_put_fd(client); if (result) result = send_packed_buffer(&out_pbuf, fd); @@ -1629,7 +1629,7 @@ static bool g_loaded_binary_save(GLoadedBinary *binary, xmlDoc *xdoc, xmlXPathCo /* Sauvegarde côté serveur */ if (result) - g_db_client_save(binary->local); + g_hub_client_save(binary->local); return result; diff --git a/src/analysis/binary.h b/src/analysis/binary.h index ad4f568..2c57283 100644 --- a/src/analysis/binary.h +++ b/src/analysis/binary.h @@ -117,7 +117,7 @@ bool g_loaded_binary_save_cache(const GLoadedBinary *); /* Fournit le client assurant la liaison avec un serveur. */ -GDbClient *g_loaded_binary_get_db_client(const GLoadedBinary *); +GHubClient *g_loaded_binary_get_db_client(const GLoadedBinary *); /* Fournit l'ensemble des collections utilisées par un binaire. */ GDbCollection **g_loaded_binary_get_all_collections(const GLoadedBinary *, size_t *); diff --git a/src/analysis/contents/file.c b/src/analysis/contents/file.c index 951828f..5509455 100644 --- a/src/analysis/contents/file.c +++ b/src/analysis/contents/file.c @@ -294,7 +294,7 @@ static void g_file_content_finalize(GFileContent *content) GBinContent *g_file_content_new(const char *filename) { - GFileContent *result; /* Structure à retourner */ + GFileContent *result; /* Structure à retourner */ int fd; /* Descripteur du fichier */ struct stat info; /* Informations sur le fichier */ int ret; /* Bilan d'un appel */ diff --git a/src/analysis/db/Makefile.am b/src/analysis/db/Makefile.am index 53d7c36..2f7cddd 100644 --- a/src/analysis/db/Makefile.am +++ b/src/analysis/db/Makefile.am @@ -3,6 +3,7 @@ noinst_LTLIBRARIES = libanalysisdb.la libanalysiskeys.la libanalysisdb_la_SOURCES = \ + auth.h auth.c \ cdb.h cdb.c \ certs.h certs.c \ client.h client.c \ diff --git a/src/analysis/db/auth.c b/src/analysis/db/auth.c new file mode 100644 index 0000000..af51af6 --- /dev/null +++ b/src/analysis/db/auth.c @@ -0,0 +1,705 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * auth.c - mise en place et gestion des autorisations pour les partages + * + * Copyright (C) 2019 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#include "auth.h" + + +#include <fcntl.h> +#include <glib.h> +#include <pwd.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/stat.h> +#include <sys/wait.h> + + +#include "../../common/extstr.h" +#include "../../common/io.h" +#include "../../common/pathname.h" +#include "../../common/xdg.h" +#include "../../core/logs.h" + + + +/* Fournit le répertoire d'enregistrement des certificats. */ +static char *get_cert_storage_directory(const char *, const char *, const char *); + +/* Calcule l'empreinte d'un fichier de demande de signature. */ +static char *compute_csr_fingerprint(const char *); + + + +/****************************************************************************** +* * +* Paramètres : addr = adresse UNIX constituée. [OUT] * +* * +* Description : Met en place un canal UNIX pour un serveur interne. * +* * +* Retour : Bilan de la définition. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool build_internal_server_socket(struct sockaddr_un *addr) +{ + bool result; /* Bilan à retourner */ + char *suffix; /* Fin de la destination */ + char *path; /* Chemin d'accès au canal */ + int ret; /* Bilan intermédiaire */ + size_t length; /* Taille du chemin complet */ + + suffix = strdup("chrysalide"); + suffix = stradd(suffix, G_DIR_SEPARATOR_S); + suffix = stradd(suffix, "internal-server"); + + path = get_xdg_config_dir(suffix); + + free(suffix); + + ret = ensure_path_exists(path); + if (ret != 0) goto mts_exit; + + length = strlen(path) + 1; + +#ifndef UNIX_PATH_MAX +# define UNIX_PATH_MAX 108 +#endif + + if (length > UNIX_PATH_MAX) + { + log_variadic_message(LMT_ERROR, + _("Impossible to use '%s' as UNIX socket path: string is too long ! (%zu vs %u)\n"), + path, length, UNIX_PATH_MAX); + goto mts_exit; + } + + memset(addr, 0, sizeof(struct sockaddr_un)); + + addr->sun_family = AF_UNIX; + strncpy(addr->sun_path, path, UNIX_PATH_MAX - 1); + + result = true; + + mts_exit: + + free(path); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : type = type de certificat à gérer. * +* host = dénomination du serveur visé ou NULL. * +* port = port d'écoute ou NULL. * +* sub = éventuelle sous-partie ou NULL. * +* * +* Description : Fournit le répertoire de travail pour les données d'analyse. * +* * +* Retour : Définition d'emplacement à libérer de la mémoire après usage.* +* * +* Remarques : - * +* * +******************************************************************************/ + +char *get_db_working_directory(const char *type, const char *host, const char *port, const char *sub) +{ + char *result; /* Chemin à retourner */ + char *suffix; /* Fin de la destination */ + + suffix = strdup("chrysalide"); + suffix = stradd(suffix, G_DIR_SEPARATOR_S); + suffix = stradd(suffix, type); + suffix = stradd(suffix, G_DIR_SEPARATOR_S); + + if (host != NULL) + { + suffix = stradd(suffix, host); + + if (port == NULL) + suffix = stradd(suffix, G_DIR_SEPARATOR_S); + + } + + if (port != NULL) + { + suffix = stradd(suffix, "-"); + suffix = stradd(suffix, port); + suffix = stradd(suffix, G_DIR_SEPARATOR_S); + } + + if (sub != NULL) + { + suffix = stradd(suffix, sub); + suffix = stradd(suffix, G_DIR_SEPARATOR_S); + } + + result = get_xdg_config_dir(suffix); + + free(suffix); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : outdir = répertoire de sortie pour les nouveaux fichiers. * +* host = dénomination du serveur visé. * +* port = port d'écoute ou NULL. * +* * +* Description : Fournit le répertoire d'enregistrement des certificats. * +* * +* Retour : Définition d'emplacement à libérer de la mémoire après usage.* +* * +* Remarques : - * +* * +******************************************************************************/ + +static char *get_cert_storage_directory(const char *outdir, const char *host, const char *port) +{ + char *result; /* Chemin à retourner */ + + result = strdup(outdir); + + if (!endswith(result, G_DIR_SEPARATOR_S)) + result = stradd(result, G_DIR_SEPARATOR_S); + + result = stradd(result, host); + + if (port == NULL) + result = stradd(result, G_DIR_SEPARATOR_S); + + else + { + result = stradd(result, "-"); + result = stradd(result, port); + result = stradd(result, G_DIR_SEPARATOR_S); + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : valid = durée de validité des certificats. * +* entries = éléments d'identité à utiliser pour l'opération. * +* * +* Description : Etablit une base pour l'identité de l'utilisateur. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool setup_client_identity(unsigned long valid, x509_entries *entries) +{ + bool result; /* Bilan de l'opération */ + char *working; /* Répertoire pour le client */ + uid_t uid; /* Identifiant d'utilisateur */ + struct passwd *pw; /* Indications sur l'usager */ + + working = get_db_working_directory("clients", NULL, NULL, NULL); + + result = mkpath(working); + + if (result) + { + if (entries->common_name == NULL) + { + uid = geteuid(); + pw = getpwuid(uid); + + if (pw != NULL) + { + log_variadic_message(LMT_WARNING, + _("Replaced the empty identity common name with '%s'"), + pw->pw_name); + + entries->common_name = strdup(pw->pw_name); + + } + + } + + result = build_keys_and_request(working, "client", entries); + + } + + free(working); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : host = désignation du serveur à contacter. * +* port = port d'écoute correspondant. * +* valid = durée de validité des certificats. * +* entries = éléments d'identité à utiliser pour l'opération. * +* * +* Description : Etablit une base pour l'identité d'un serveur. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool setup_server_identity(const char *host, const char *port, unsigned long valid, x509_entries *entries) +{ + bool result; /* Bilan de l'opération */ + char *working; /* Répertoire pour le serveur */ + char *csr; /* Requête de signature */ + char *old; /* Conservation de l'origine */ + char *new; /* Nouvelle désignation */ + char *cacert; /* Certificat d'autorité */ + char *cakey; /* Clef de cette autorité */ + char *cert; /* Certificat signé en sortie */ + + if (host == NULL) + { + host = "standalone"; + port = NULL; + } + + else if (strcmp(host, "standalone") != 0 && port == NULL) + port = "1337"; + + working = get_db_working_directory("servers", host, port, NULL); + + result = mkpath(working); + + if (result) + { + if (entries->common_name == NULL) + { + log_variadic_message(LMT_WARNING, + _("Replaced the empty identity common name with '%s'"), + host); + + entries->common_name = strdup(host); + + } + + old = entries->common_name; + + new = strdup(old); + new = stradd(new, " CA"); + + entries->common_name = new; + + result = build_keys_and_ca(working, "ca", valid, entries); + + entries->common_name = old; + + free(new); + + if (result) + result = build_keys_and_request(working, "server", entries); + + if (result) + { + csr = build_absolute_filename(working, "server-csr.pem"); + cacert = build_absolute_filename(working, "ca-cert.pem"); + cakey = build_absolute_filename(working, "ca-key.pem"); + cert = build_absolute_filename(working, "server-cert.pem"); + + result = sign_cert(csr, cacert, cakey, cert, valid); + + free(csr); + free(cacert); + free(cakey); + free(cert); + + } + + } + + free(working); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : csr = fichier contenant le certificat à signer. * +* * +* Description : Calcule l'empreinte d'un fichier de demande de signature. * +* * +* Retour : Empreinte calculée ou NULL en cas d'erreur. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static char *compute_csr_fingerprint(const char *csr) +{ + char *result; /* Empreinte à retourner */ + int fd; /* Descripteur du fichier */ + struct stat info; /* Informations sur le fichier */ + int ret; /* Bilan d'un appel */ + void *data; /* Quantité de données traitées*/ + bool status; /* Bilan de la lecture */ + + result = NULL; + + fd = open(csr, O_RDONLY); + if (fd == -1) + { + LOG_ERROR_N("open"); + goto exit; + } + + ret = fstat(fd, &info); + if (ret == -1) + { + LOG_ERROR_N("fstat"); + goto done; + } + + data = malloc(info.st_size); + + status = safe_read(fd, data, info.st_size); + + if (status) + result = g_compute_checksum_for_data(G_CHECKSUM_SHA256, data, info.st_size); + + free(data); + + done: + + close(fd); + + exit: + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : host = désignation du serveur à contacter. * +* port = port d'écoute correspondant. * +* valid = durée de validité des certificats. * +* csr = fichier contenant le certificat à signer. * +* outdir = répertoire de sortie pour les nouveaux fichiers. * +* * +* Description : Ajoute un certificat dans les utilisateurs d'un serveur. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool add_client_to_server(const char *host, const char *port, unsigned long valid, const char *csr, const char *outdir) +{ + bool result; /* Bilan de l'opération */ + char *hash; /* Empreinte de la requête */ + char *working; /* Répertoire pour le serveur */ + char *cacert; /* Certificat d'autorité */ + char *cakey; /* Clef de cette autorité */ + char *storage; /* Répertoire de stockage */ + char *dest; /* Destination d'une copie */ + + result = false; + + if (host == NULL) + { + host = "standalone"; + port = NULL; + } + + else if (strcmp(host, "standalone") != 0 && port == NULL) + port = "1337"; + + hash = compute_csr_fingerprint(csr); + if (hash == NULL) goto exit; + + working = get_db_working_directory("servers", host, port, "authorized"); + + result = mkpath(working); + + if (result) + { + hash = strprep(hash, working); + hash = stradd(hash, "-cert.pem"); + + free(working); + + working = get_db_working_directory("servers", host, port, NULL); + + cacert = build_absolute_filename(working, "ca-cert.pem"); + cakey = build_absolute_filename(working, "ca-key.pem"); + + result = sign_cert(csr, cacert, cakey, hash, valid); + + if (result) + { + storage = get_cert_storage_directory(outdir, host, port); + + result = mkpath(storage); + + if (result) + { + dest = build_absolute_filename(storage, "ca-cert.pem"); + + result = copy_file(dest, cacert); + + free(dest); + + } + + if (result) + { + dest = build_absolute_filename(storage, "client-cert.pem"); + + result = copy_file(dest, hash); + + free(dest); + + } + + free(storage); + + } + + free(cacert); + free(cakey); + + free(hash); + + } + + free(working); + + exit: + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Assure la présence d'unenvironnement pour serveur interne. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool ensure_internal_connections_setup(void) +{ + bool result; /* Bilan à retourner */ + unsigned long valid; /* Durée de validité */ + char *filename; /* Fichier devant être présent */ + int ret; /* Bilan d'une validation */ + x509_entries identity; /* Nouvelle identité à pousser */ + bool status; /* Bilan intermédiaire */ + char *csr; /* Fichier de requête */ + char *outdir; /* Répertoire de sortie */ + + result = false; + + valid = 3 * 365 * 24 * 60 * 60; + + /* Teste la présence d'une identitié pour le client */ + + filename = get_db_working_directory("clients", NULL, NULL, NULL); + filename = stradd(filename, "client-csr.pem"); + + ret = access(filename, R_OK); + + if (ret != 0) + { + memset(&identity, 0, sizeof(identity)); + + status = setup_client_identity(valid, &identity); + + free_x509_entries(&identity); + + if (status) + ret = access(filename, R_OK); + else + ret = -1; + + } + + free(filename); + + if (ret != 0) + goto done; + + /* Teste la présence d'une identitié pour le serveur interne */ + + filename = get_db_working_directory("servers", "standalone", NULL, NULL); + filename = stradd(filename, "server-csr.pem"); + + ret = access(filename, R_OK); + + if (ret != 0) + { + memset(&identity, 0, sizeof(identity)); + + status = setup_server_identity("standalone", NULL, valid, &identity); + + free_x509_entries(&identity); + + if (status) + ret = access(filename, R_OK); + else + ret = -1; + + } + + free(filename); + + if (ret != 0) + goto done; + + /* Teste la présence d'une autorisation pour l'accès à ce serveur */ + + filename = get_db_working_directory("clients", "standalone", NULL, NULL); + filename = stradd(filename, "client-cert.pem"); + + ret = access(filename, R_OK); + + if (ret != 0) + { + csr = get_db_working_directory("clients", NULL, NULL, NULL); + csr = stradd(csr, "client-csr.pem"); + + outdir = get_db_working_directory("clients", NULL, NULL, NULL); + + status = add_client_to_server("standalone", NULL, valid, csr, outdir); + + free(outdir); + free(csr); + + if (status) + ret = access(filename, R_OK); + else + ret = -1; + + } + + free(filename); + + if (ret != 0) + goto done; + + result = true; + + done: + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Lance un serveur interne si besoin est. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool launch_internal_server(void) +{ + bool result; /* Bilan à retourner */ + pid_t child; /* Identifiant de processus */ + const char *prgm; /* Programme à exécuter */ + int wstatus; /* Etat du serveur lancé */ + pid_t ret; /* Bilan d'un appel */ + + char * const argv[] = { + "chrysalide-hub", + "run", + NULL + }; + + child = fork(); + + switch (child) + { + case -1: + result = false; + LOG_ERROR_N("fork"); + break; + + case 0: +#ifndef DISCARD_LOCAL + prgm = PACKAGE_SOURCE_DIR "/src/chrysalide-hub"; +#else + prgm = "chrysalide-hub"; +#endif + + execvp(prgm, argv); + + LOG_ERROR_N("execvp"); + exit(EXIT_FAILURE); + break; + + default: + + ret = waitpid(child, &wstatus, 0); + + if (ret == -1) + { + result = false; + LOG_ERROR_N("waitpid"); + } + + else + result = (WEXITSTATUS(wstatus) == EXIT_SUCCESS); + + break; + + } + + return result; + +} diff --git a/src/analysis/db/auth.h b/src/analysis/db/auth.h new file mode 100644 index 0000000..4db7af7 --- /dev/null +++ b/src/analysis/db/auth.h @@ -0,0 +1,59 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * auth.h - prototypes pour la mise en place et gestion des autorisations pour les partages + * + * Copyright (C) 2019 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _ANALYSIS_DB_AUTH_H +#define _ANALYSIS_DB_AUTH_H + + +#include <stdbool.h> +#include <sys/un.h> + + +#include "certs.h" + + + +/* Met en place un canal UNIX pour un serveur interne. */ +bool build_internal_server_socket(struct sockaddr_un *); + +/* Fournit le répertoire de travail pour les données d'analyse. */ +char *get_db_working_directory(const char *, const char *, const char *, const char *); + +/* Etablit une base pour l'identité de l'utilisateur. */ +bool setup_client_identity(unsigned long, x509_entries *); + +/* Etablit une base pour l'identité d'un serveur. */ +bool setup_server_identity(const char *, const char *, unsigned long, x509_entries *); + +/* Ajoute un certificat dans les utilisateurs d'un serveur. */ +bool add_client_to_server(const char *, const char *, unsigned long, const char *, const char *); + +/* Assure la présence d'unenvironnement pour serveur interne. */ +bool ensure_internal_connections_setup(void); + +/* Lance un serveur interne si besoin est. */ +bool launch_internal_server(void); + + + +#endif /* _ANALYSIS_DB_AUTH_H */ diff --git a/src/analysis/db/cdb.c b/src/analysis/db/cdb.c index c2855f7..74e8501 100644 --- a/src/analysis/db/cdb.c +++ b/src/analysis/db/cdb.c @@ -51,7 +51,6 @@ #include "../../common/xml.h" #include "../../core/collections.h" #include "../../core/logs.h" -#include "../../core/params.h" @@ -59,7 +58,7 @@ typedef struct _cdb_client { SSL *ssl_fd; /* Canal de communication */ - rle_string user; /* Utilisateur à l'autre bout */ + char *user; /* Utilisateur à l'autre bout */ uint64_t last_time; /* Date de dernier envoi */ @@ -125,7 +124,7 @@ static bool g_cdb_archive_read(GCdbArchive *); /* Crée la description XML correspondant à l'archive. */ -static bool g_cdb_archive_create_xml_desc(GCdbArchive *, const rle_string *); +static bool g_cdb_archive_create_xml_desc(GCdbArchive *); /* Vérifie la conformité d'une description XML avec le serveur. */ static bool g_cdb_archive_check_xml_version(const GCdbArchive *); @@ -263,7 +262,7 @@ static void g_cdb_archive_finalize(GCdbArchive *archive) if (archive->xml_desc != NULL) { ret = unlink(archive->xml_desc); - if (ret != 0) perror("unlink"); + if (ret != 0) LOG_ERROR_N("unlink"); free(archive->xml_desc); @@ -272,7 +271,7 @@ static void g_cdb_archive_finalize(GCdbArchive *archive) if (archive->sql_db != NULL) { ret = unlink(archive->sql_db); - if (ret != 0) perror("unlink"); + if (ret != 0) LOG_ERROR_N("unlink"); free(archive->sql_db); @@ -290,8 +289,8 @@ static void g_cdb_archive_finalize(GCdbArchive *archive) /****************************************************************************** * * * Paramètres : basedir = répertoire de stockage des enregistrements. * +* tmpdir = répertoire de travail temporaire. * * hash = empreinte du binaire à représenter. * -* user = désignation d'un éventuel nouveau créateur. * * error = indication éventuelle en cas d'échec. [OUT] * * * * Description : Définit ou ouvre une archive d'éléments utilisateur. * @@ -303,11 +302,9 @@ static void g_cdb_archive_finalize(GCdbArchive *archive) * * ******************************************************************************/ -GCdbArchive *g_cdb_archive_new(const char *basedir, const rle_string *hash, const rle_string *user, DBError *error) +GCdbArchive *g_cdb_archive_new(const char *basedir, const char *tmpdir, const rle_string *hash, DBError *error) { GCdbArchive *result; /* Adresse à retourner */ - const char *tmpdir; /* Répertoire d'accueil */ - bool status; /* Bilan d'un consultation */ int ret; /* Retour d'un appel */ struct stat finfo; /* Information sur l'archive */ @@ -318,7 +315,6 @@ GCdbArchive *g_cdb_archive_new(const char *basedir, const rle_string *hash, cons /* Chemin de l'archive */ result->filename = strdup(basedir); - result->filename = stradd(result->filename, G_DIR_SEPARATOR_S); result->filename = stradd(result->filename, hash->data); result->filename = stradd(result->filename, ".cdb.tar.xz"); @@ -327,8 +323,8 @@ GCdbArchive *g_cdb_archive_new(const char *basedir, const rle_string *hash, cons /* Chemin des enregistrements temporaires */ - status = g_generic_config_get_value(get_main_configuration(), MPK_TMPDIR, &tmpdir); - if (!status) goto gcan_no_tmp; + if (!mkpath(tmpdir)) + goto gcan_error; ret = asprintf(&result->xml_desc, "%s" G_DIR_SEPARATOR_S "%s_desc.xml", tmpdir, result->hash.data); if (ret == -1) goto gcan_no_tmp; @@ -348,7 +344,7 @@ GCdbArchive *g_cdb_archive_new(const char *basedir, const rle_string *hash, cons /* Le soucis ne vient pas de l'absence du fichier... */ if (errno != ENOENT) goto gcan_error; - g_cdb_archive_create_xml_desc(result, user); + g_cdb_archive_create_xml_desc(result); g_cdb_archive_create_db(result); *error = g_cdb_archive_write(result); @@ -573,7 +569,6 @@ int g_cdb_archive_compare_hash(const GCdbArchive *archive, const rle_string *has /****************************************************************************** * * * Paramètres : archive = archive à constituer. * -* user = désignation d'un éventuel nouveau créateur. * * * * Description : Crée la description XML correspondant à l'archive. * * * @@ -583,34 +578,38 @@ int g_cdb_archive_compare_hash(const GCdbArchive *archive, const rle_string *has * * ******************************************************************************/ -static bool g_cdb_archive_create_xml_desc(GCdbArchive *archive, const rle_string *user) +static bool g_cdb_archive_create_xml_desc(GCdbArchive *archive) { bool result; /* Bilan à retourner */ timestamp_t timestamp; /* Date de création */ char tmp[sizeof(XSTR(UINT64_MAX))]; /* Stockage temporaire */ result = create_new_xml_file(&archive->xdoc, &archive->context); - if (!result) return false; - result &= add_content_to_node(archive->xdoc, archive->context, - "/ChrysalideBinary/Version", PACKAGE_VERSION); + if (result) + result = add_content_to_node(archive->xdoc, archive->context, + "/ChrysalideBinary/Version", PACKAGE_VERSION); - result &= add_content_to_node(archive->xdoc, archive->context, - "/ChrysalideBinary/Protocol", XSTR(CDB_PROTOCOL_VERSION)); + if (result) + result = add_content_to_node(archive->xdoc, archive->context, + "/ChrysalideBinary/Protocol", XSTR(CDB_PROTOCOL_VERSION)); - result &= add_content_to_node(archive->xdoc, archive->context, - "/ChrysalideBinary/Hash", archive->hash.data); + if (result) + result = add_content_to_node(archive->xdoc, archive->context, + "/ChrysalideBinary/Hash", archive->hash.data); - result &= add_content_to_node(archive->xdoc, archive->context, - "/ChrysalideBinary/Creation/Author", user->data); + if (result) + { + init_timestamp(×tamp); + snprintf(tmp, sizeof(tmp), "%" PRIu64, timestamp); - init_timestamp(×tamp); - snprintf(tmp, sizeof(tmp), "%" PRIu64, timestamp); + result = add_content_to_node(archive->xdoc, archive->context, + "/ChrysalideBinary/CreationDate", tmp); - result &= add_content_to_node(archive->xdoc, archive->context, - "/ChrysalideBinary/Creation/Date", tmp); + } - save_xml_file(archive->xdoc, archive->xml_desc); + if (result) + result = save_xml_file(archive->xdoc, archive->xml_desc); return result; @@ -863,7 +862,7 @@ static void *g_cdb_archive_process(GCdbArchive *archive) { if (errno == EINTR) continue; - perror("poll"); + LOG_ERROR_N("poll"); break; } @@ -874,7 +873,15 @@ static void *g_cdb_archive_process(GCdbArchive *archive) { /* Le canal est fermé, une sortie doit être demandée... */ if (fds[i].revents & POLLNVAL) - goto gcap_closed_exchange; + goto closed_exchange; + + /** + * Même chose, cf. "TCP: When is EPOLLHUP generated?" + * https://stackoverflow.com/questions/52976152/tcp-when-is-epollhup-generated/52976327#52976327 + */ + + if (fds[i].revents & (POLLHUP | POLLRDHUP)) + goto closed_exchange; /* Données présentes en entrée */ if (fds[i].revents & (POLLIN | POLLPRI)) @@ -979,7 +986,7 @@ static void *g_cdb_archive_process(GCdbArchive *archive) exit_packed_buffer(&in_pbuf); - gcap_closed_exchange: + closed_exchange: g_cdb_archive_remove_client(archive, i); @@ -1012,7 +1019,6 @@ static void *g_cdb_archive_process(GCdbArchive *archive) * * * Paramètres : archive = archive à connecter avec un utilisateur. * * fd = canal de communication réseau ouvert. * -* user = désignation de l'utilisateur associé. * * * * Description : Associe un nouvel utilisateur à l'archive. * * * @@ -1022,8 +1028,9 @@ static void *g_cdb_archive_process(GCdbArchive *archive) * * ******************************************************************************/ -void g_cdb_archive_add_client(GCdbArchive *archive, SSL *fd, const rle_string *user) +void g_cdb_archive_add_client(GCdbArchive *archive, SSL *fd) { + X509 *peer_cert; /* Certificat présenté */ volatile pthread_t *process_id; /* Identifiant de la procédure */ /** @@ -1051,7 +1058,12 @@ void g_cdb_archive_add_client(GCdbArchive *archive, SSL *fd, const rle_string *u archive->clients = realloc(archive->clients, ++archive->count * sizeof(cdb_client)); archive->clients[archive->count - 1].ssl_fd = fd; - dup_into_rle_string(&archive->clients[archive->count - 1].user, get_rle_string(user)); + + peer_cert = SSL_get_peer_certificate(fd); + + archive->clients[archive->count - 1].user = X509_NAME_oneline(X509_get_subject_name(peer_cert), NULL, -1); + + X509_free(peer_cert); /* Démarrage ou redémarrage du processus d'écoute */ @@ -1099,7 +1111,7 @@ static void g_cdb_archive_remove_client(GCdbArchive *archive, size_t index) g_mutex_lock(&archive->clients_access); SSL_free(client->ssl_fd); - exit_rle_string(&client->user); + free(client->user); if ((index + 1) < archive->count) memmove(&archive->clients[index], &archive->clients[index + 1], diff --git a/src/analysis/db/cdb.h b/src/analysis/db/cdb.h index 601b10f..8d4a86b 100644 --- a/src/analysis/db/cdb.h +++ b/src/analysis/db/cdb.h @@ -54,7 +54,7 @@ typedef struct _GCdbArchiveClass GCdbArchiveClass; GType g_cdb_archive_get_type(void); /* Prépare un client pour une connexion à une BD. */ -GCdbArchive *g_cdb_archive_new(const char *, const rle_string *, const rle_string *, DBError *); +GCdbArchive *g_cdb_archive_new(const char *, const char *, const rle_string *, DBError *); /* Enregistre une archive avec tous les éléments à conserver. */ DBError g_cdb_archive_write(const GCdbArchive *); @@ -67,7 +67,7 @@ int g_cdb_archive_compare_hash(const GCdbArchive *, const rle_string *); /* Associe un nouvel utilisateur à l'archive. */ -void g_cdb_archive_add_client(GCdbArchive *, SSL *, const rle_string *); +void g_cdb_archive_add_client(GCdbArchive *, SSL *); diff --git a/src/analysis/db/certs.c b/src/analysis/db/certs.c index 41a40b7..a333d9a 100644 --- a/src/analysis/db/certs.c +++ b/src/analysis/db/certs.c @@ -252,7 +252,7 @@ static RSA *generate_rsa_key(unsigned int bits, unsigned long e) * * ******************************************************************************/ -bool make_ca(const char *dir, const char *label, unsigned long valid, const x509_entries *entries) +bool build_keys_and_ca(const char *dir, const char *label, unsigned long valid, const x509_entries *entries) { RSA *rsa; /* Clef RSA pour le certificat */ EVP_PKEY *pk; /* Enveloppe pour clef publique*/ @@ -455,7 +455,7 @@ static bool add_extension_to_req(STACK_OF(X509_EXTENSION) *sk, int nid, /*const * * ******************************************************************************/ -bool make_request(const char *dir, const char *label, const x509_entries *entries) +bool build_keys_and_request(const char *dir, const char *label, const x509_entries *entries) { RSA *rsa; /* Clef RSA pour le certificat */ EVP_PKEY *pk; /* Enveloppe pour clef publique*/ diff --git a/src/analysis/db/certs.h b/src/analysis/db/certs.h index 31da82f..132cd7f 100644 --- a/src/analysis/db/certs.h +++ b/src/analysis/db/certs.h @@ -49,10 +49,10 @@ bool are_x509_entries_empty(const x509_entries *); void free_x509_entries(x509_entries *); /* Crée un certificat de signature racine. */ -bool make_ca(const char *, const char *, unsigned long, const x509_entries *); +bool build_keys_and_ca(const char *, const char *, unsigned long, const x509_entries *); /* Crée un certificat pour application. */ -bool make_request(const char *, const char *, const x509_entries *); +bool build_keys_and_request(const char *, const char *, const x509_entries *); /* Signe un certificat pour application. */ bool sign_cert(const char *, const char *, const char *, const char *, unsigned long); diff --git a/src/analysis/db/client.c b/src/analysis/db/client.c index 24198f6..c608bfa 100644 --- a/src/analysis/db/client.c +++ b/src/analysis/db/client.c @@ -37,31 +37,39 @@ #include <i18n.h> -#include "keymgn.h" +#include "auth.h" #include "protocol.h" #include "misc/rlestr.h" +#include "../../common/extstr.h" #include "../../common/io.h" #include "../../common/xdg.h" #include "../../core/logs.h" +/* Format générique des adresses de connexion */ +typedef union _gen_sockaddr_t +{ + struct sockaddr_in inet4_addr; /* Adresse d'écoute IPv4 */ + struct sockaddr_in6 inet6_addr; /* Adresse d'écoute IPv6 */ + struct sockaddr inet_4_6_addr; /* Adresse d'écoute IPv4/6 */ + +} gen_sockaddr_t; + + /* Description de client à l'écoute (instance) */ -struct _GDbClient +struct _GHubClient { 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é */ GList *collections; /* Collections d'un binaire */ + 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 */ SSL *tls_fd; /* Même canal, mais sécurisé */ @@ -74,7 +82,7 @@ struct _GDbClient }; /* Description de client à l'écoute (classe) */ -struct _GDbClientClass +struct _GHubClientClass { GObjectClass parent; /* A laisser en premier */ @@ -82,24 +90,27 @@ struct _GDbClientClass /* Initialise la classe des descriptions de fichier binaire. */ -static void g_db_client_class_init(GDbClientClass *); +static void g_hub_client_class_init(GHubClientClass *); /* Initialise une description de fichier binaire. */ -static void g_db_client_init(GDbClient *); +static void g_hub_client_init(GHubClient *); + +/* Supprime toutes les références externes. */ +static void g_hub_client_dispose(GHubClient *); /* Procède à la libération totale de la mémoire. */ -static void g_db_client_finalize(GDbClient *); +static void g_hub_client_finalize(GHubClient *); /* Démarre réellement la connexion à la base de données. */ -static bool g_db_client_start_common(GDbClient *, char *); +static bool g_hub_client_start_common(GHubClient *, char *); /* Assure l'accueil des nouvelles mises à jour. */ -static void *g_db_client_update(GDbClient *); +static void *g_hub_client_update(GHubClient *); /* Indique le type défini pour une description de client à l'écoute. */ -G_DEFINE_TYPE(GDbClient, g_db_client, G_TYPE_OBJECT); +G_DEFINE_TYPE(GHubClient, g_hub_client, G_TYPE_OBJECT); /****************************************************************************** @@ -114,13 +125,14 @@ G_DEFINE_TYPE(GDbClient, g_db_client, G_TYPE_OBJECT); * * ******************************************************************************/ -static void g_db_client_class_init(GDbClientClass *klass) +static void g_hub_client_class_init(GHubClientClass *klass) { GObjectClass *object; /* Autre version de la classe */ object = G_OBJECT_CLASS(klass); - object->finalize = (GObjectFinalizeFunc)g_db_client_finalize; + object->dispose = (GObjectFinalizeFunc/* ! */)g_hub_client_dispose; + object->finalize = (GObjectFinalizeFunc)g_hub_client_finalize; } @@ -137,11 +149,11 @@ static void g_db_client_class_init(GDbClientClass *klass) * * ******************************************************************************/ -static void g_db_client_init(GDbClient *client) +static void g_hub_client_init(GHubClient *client) { + client->working = NULL; + client->tls_ctx = NULL; - client->cert_file = NULL; - client->key_file = NULL; client->fd = -1; client->tls_fd = NULL; @@ -152,6 +164,27 @@ static void g_db_client_init(GDbClient *client) /****************************************************************************** * * +* Paramètres : archive = instance d'objet GLib à traiter. * +* * +* Description : Supprime toutes les références externes. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_hub_client_dispose(GHubClient *client) +{ + g_hub_client_stop(client); + + G_OBJECT_CLASS(g_hub_client_parent_class)->dispose(G_OBJECT(client)); + +} + + +/****************************************************************************** +* * * Paramètres : client = instance d'objet GLib à traiter. * * * * Description : Procède à la libération totale de la mémoire. * @@ -162,36 +195,28 @@ static void g_db_client_init(GDbClient *client) * * ******************************************************************************/ -static void g_db_client_finalize(GDbClient *client) +static void g_hub_client_finalize(GHubClient *client) { - free(client->author); - unset_rle_string(&client->hash); - assert(client->tls_ctx == NULL); - - if (client->cert_file != NULL) - free(client->cert_file); + if (client->working != NULL) + free(client->working); - if (client->key_file != NULL) - free(client->key_file); + assert(client->tls_ctx == NULL); assert(client->tls_fd == NULL); if (client->desc != NULL) free(client->desc); - G_OBJECT_CLASS(g_db_client_parent_class)->finalize(G_OBJECT(client)); + G_OBJECT_CLASS(g_hub_client_parent_class)->finalize(G_OBJECT(client)); } /****************************************************************************** * * -* 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. * +* Paramètres : hash = empreinte d'un binaire en cours d'analyse. * * collections = ensemble de collections existantes. * * * * Description : Prépare un client pour une connexion à une BD. * @@ -202,16 +227,11 @@ static void g_db_client_finalize(GDbClient *client) * * ******************************************************************************/ -GDbClient *g_db_client_new(char *author, char *kfile, const char *name, const char *hash, GList *collections) +GHubClient *g_hub_client_new(const char *hash, GList *collections) { - GDbClient *result; /* Adresse à retourner */ + GHubClient *result; /* Adresse à retourner */ - result = g_object_new(G_TYPE_DB_CLIENT, NULL); - - result->author = author; - result->key_file = kfile; - - result->name = name; + result = g_object_new(G_TYPE_HUB_CLIENT, NULL); init_static_rle_string(&result->hash, hash); result->collections = collections; @@ -233,7 +253,7 @@ GDbClient *g_db_client_new(char *author, char *kfile, const char *name, const ch * * ******************************************************************************/ -bool g_db_client_start_internal(GDbClient *client) +bool g_hub_client_start_internal(GHubClient *client) { bool status; /* Bilan de la connexion */ struct sockaddr_un addr; /* Adresse de transmission */ @@ -242,42 +262,43 @@ bool g_db_client_start_internal(GDbClient *client) /* Identification du serveur à contacter */ - status = build_tmp_socket("internal-server", &addr); - if (!status) goto gdcni_error; + status = build_internal_server_socket(&addr); + if (!status) goto fs_error; /* Création d'un canal de communication */ client->fd = socket(AF_UNIX, SOCK_STREAM, 0); if (client->fd == -1) { - perror("socket"); - goto gdcni_error; + LOG_ERROR_N("socket"); + goto fs_error; } ret = connect(client->fd, (struct sockaddr *)&addr, sizeof(struct sockaddr_un)); if (ret == -1) { - perror("connect"); - goto gdcsi_no_listening; + LOG_ERROR_N("connect"); + goto no_listening; } asprintf(&desc, "unix://%s", addr.sun_path); - status = g_db_client_start_common(client, desc); + client->working = get_db_working_directory("clients", "standalone", NULL, NULL); + + status = g_hub_client_start_common(client, desc); if (!status) - goto gdcsi_no_listening; + goto no_listening; return true; - gdcsi_no_listening: + no_listening: close(client->fd); - - gdcni_error: - client->fd = -1; + fs_error: + return false; } @@ -288,6 +309,7 @@ bool g_db_client_start_internal(GDbClient *client) * 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. * +* ipv6 = adopte une préférence pour les adresses IPv6. * * * * Description : Démarre la connexion à la base de données distante. * * * @@ -297,54 +319,114 @@ bool g_db_client_start_internal(GDbClient *client) * * ******************************************************************************/ -bool g_db_client_start_remote(GDbClient *client, const char *host, unsigned short port) +bool g_hub_client_start_remote(GHubClient *client, const char *host, const char *port, bool ipv6) { - struct hostent *hp; /* Informations sur l'hôte */ - struct sockaddr_in addr; /* Adresse de transmission */ - int ret; /* Bilan d'un appel */ + struct addrinfo hints; /* Cadre de connexion souhaité */ + struct addrinfo *available; /* Cadres de connexion dispos */ + int ret; /* Bilan d'une consultation */ + int domain; /* Domaine du canal */ + struct addrinfo *iter; /* Boucle de parcours */ + gen_sockaddr_t addr; /* Adresse d'écoute générique */ + socklen_t sock_len; /* Taille de cette adresse */ char *desc; /* Description du serveur ciblé*/ bool status; /* Bilan de la connexion */ - /* Identification du serveur à contacter */ + /* Détermination du point d'écoute */ + + memset(&hints, 0, sizeof(hints)); + + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + + ret = getaddrinfo(host, port, &hints, &available); + if (ret != 0) + { + LOG_ERROR_GAI_N("getaddrinfo", ret); + goto no_target; + } + + domain = AF_UNSPEC; + + /** + * Premier tour : on essaie de se plier à la demande. + */ + + for (iter = available; iter != NULL && domain == AF_UNSPEC; iter = iter->ai_next) + { + if (ipv6 && iter->ai_family != AF_INET6) + continue; - hp = gethostbyname(host); - if (hp == NULL) return false; + if (!ipv6 && iter->ai_family != AF_INET) + continue; + + domain = iter->ai_family; + + memcpy(&addr.inet_4_6_addr, iter->ai_addr, iter->ai_addrlen); + sock_len = iter->ai_addrlen; + + } + + /** + * Second tour : on fait avec ce qu'on a. + */ + + for (iter = available; iter != NULL && domain == AF_UNSPEC; iter = iter->ai_next) + { + if (iter->ai_family != AF_INET6 && iter->ai_family != AF_INET) + continue; + + domain = iter->ai_family; + + memcpy(&addr.inet_4_6_addr, iter->ai_addr, iter->ai_addrlen); + sock_len = iter->ai_addrlen; + + } - addr.sin_family = hp->h_addrtype; - memcpy(&addr.sin_addr, hp->h_addr_list[0], sizeof(struct in_addr)); + if (available != NULL) + freeaddrinfo(available); - addr.sin_port = htons(port); + if (domain == AF_UNSPEC) + { + log_variadic_message(LMT_ERROR, _("No suitable address found for %s:%s"), host, port); + goto no_target; + } /* Création d'un canal de communication */ - client->fd = socket(AF_INET, SOCK_STREAM, 0); + client->fd = socket(domain, SOCK_STREAM, 0); if (client->fd == -1) { - perror("socket"); - return false; + LOG_ERROR_N("socket"); + goto error_socket; } - ret = connect(client->fd, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)); + ret = connect(client->fd, (struct sockaddr *)&addr, sock_len); if (ret == -1) { - perror("connect"); - goto gdcsr_no_listening; + LOG_ERROR_N("connect"); + goto no_listening; } - asprintf(&desc, "%s:%hu", host, port); + asprintf(&desc, "%s:%s", host, port); - status = g_db_client_start_common(client, desc); + client->working = get_db_working_directory("clients", host, port, NULL); + + status = g_hub_client_start_common(client, desc); if (!status) - goto gdcsr_no_listening; + goto no_listening; return true; - gdcsr_no_listening: + no_listening: close(client->fd); client->fd = -1; + error_socket: + no_target: + return false; } @@ -364,26 +446,21 @@ bool g_db_client_start_remote(GDbClient *client, const char *host, unsigned shor * * ******************************************************************************/ -static bool g_db_client_start_common(GDbClient *client, char *desc) +static bool g_hub_client_start_common(GHubClient *client, char *desc) { const SSL_METHOD *method; /* Mode du canal sécurisé */ + char *filename; /* Fichier PEM à manipuler */ int ret; /* Bilan d'un appel */ + char *rootdir; /* Racine pour le client */ packed_buffer out_pbuf; /* Tampon d'émission */ bool status; /* Bilan d'une opération */ - 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 */ packed_buffer in_pbuf; /* Tampon de réception */ uint32_t data; /* Mot de données lues */ DBError error; /* Validation de la connexion */ client->desc = desc; - /** - * Mise en place d'un environnement sécurisé. - */ + /* Définition d'un environnement TLS */ method = TLS_client_method(); @@ -395,6 +472,54 @@ static bool g_db_client_start_common(GDbClient *client, char *desc) goto quick_error; } + filename = strdup(client->working); + filename = stradd(filename, "client-cert.pem"); + + ret = SSL_CTX_use_certificate_file(client->tls_ctx, filename, SSL_FILETYPE_PEM); + + free(filename); + + if (ret != 1) + { + LOG_ERROR_OPENSSL; + goto tls_error; + } + + rootdir = get_db_working_directory("clients", NULL, NULL, NULL); + + filename = strdup(rootdir); + filename = stradd(filename, "client-key.pem"); + + ret = SSL_CTX_use_PrivateKey_file(client->tls_ctx, filename, SSL_FILETYPE_PEM); + + free(filename); + free(rootdir); + + if (ret != 1) + { + LOG_ERROR_OPENSSL; + goto tls_error; + } + + /* Validation des certificats */ + + SSL_CTX_set_verify(client->tls_ctx, SSL_VERIFY_PEER, NULL); + + filename = strdup(client->working); + filename = stradd(filename, "ca-cert.pem"); + + ret = SSL_CTX_load_verify_locations(client->tls_ctx, filename, NULL); + + free(filename); + + if (ret != 1) + { + LOG_ERROR_OPENSSL; + goto tls_error; + } + + /* Mise en place d'un canal de communication */ + client->tls_fd = SSL_new(client->tls_ctx); if (client->tls_fd == NULL) @@ -410,7 +535,7 @@ static bool g_db_client_start_common(GDbClient *client, char *desc) if (ret != 1) { LOG_ERROR_OPENSSL; - goto tls_error; + goto ssl_error; } /** @@ -418,8 +543,6 @@ static bool g_db_client_start_common(GDbClient *client, char *desc) * - 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(). */ @@ -435,30 +558,6 @@ static bool g_db_client_start_common(GDbClient *client, char *desc) status = pack_rle_string(&client->hash, &out_pbuf); if (!status) goto setup_error; - dup_into_rle_string(&user, client->author); - - status = pack_rle_string(&user, &out_pbuf); - if (!status) goto setup_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 setup_error; - - if (!sign_md5_hash(key, md5_digest, sig)) - { - RSA_free(key); - goto setup_error; - } - - RSA_free(key); - - status = extend_packed_buffer(&out_pbuf, sig, RSA_USED_SIZE, false); - if (!status) goto setup_error; - status = ssl_send_packed_buffer(&out_pbuf, client->tls_fd); if (!status) goto setup_error; @@ -522,7 +621,7 @@ static bool g_db_client_start_common(GDbClient *client, char *desc) client->can_get_updates = false; - client->update = g_thread_try_new("cdb_client", (GThreadFunc)g_db_client_update, client, NULL); + client->update = g_thread_try_new("cdb_client", (GThreadFunc)g_hub_client_update, client, NULL); if (client->update == NULL) { log_variadic_message(LMT_ERROR, _("Failed to start a listening thread for the server '%s'!"), @@ -543,7 +642,10 @@ static bool g_db_client_start_common(GDbClient *client, char *desc) exit_packed_buffer(&out_pbuf); - unset_rle_string(&user); + ssl_error: + + SSL_free(client->tls_fd); + client->tls_fd = NULL; tls_error: @@ -552,9 +654,6 @@ static bool g_db_client_start_common(GDbClient *client, char *desc) quick_error: - close(client->fd); - client->fd = -1; - return false; } @@ -572,7 +671,7 @@ static bool g_db_client_start_common(GDbClient *client, char *desc) * * ******************************************************************************/ -static void *g_db_client_update(GDbClient *client) +static void *g_hub_client_update(GHubClient *client) { packed_buffer out_pbuf; /* Tampon d'émission */ bool status; /* Bilan d'une opération */ @@ -617,7 +716,7 @@ static void *g_db_client_update(GDbClient *client) init_packed_buffer(&in_pbuf); - while (1) + while (client->fd != -1) { ret = poll(&fds, 1, -1); if (ret != 1) continue; @@ -626,6 +725,14 @@ static void *g_db_client_update(GDbClient *client) 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)) { reset_packed_buffer(&in_pbuf); @@ -701,7 +808,7 @@ static void *g_db_client_update(GDbClient *client) exit: - g_db_client_stop(client); + g_hub_client_stop(client); exit_packed_buffer(&in_pbuf); @@ -722,13 +829,14 @@ static void *g_db_client_update(GDbClient *client) * * ******************************************************************************/ -void g_db_client_stop(GDbClient *client) +void g_hub_client_stop(GHubClient *client) { + int fd; /* Canal à clôturer */ int ret; /* Bilan d'un appel */ /* Canal de communication */ - if (client->fd != -1) + if (client->fd == -1) { /** * Si la fermture est forcée, le thread de traitement va terminer en erreur. @@ -741,15 +849,20 @@ void g_db_client_stop(GDbClient *client) return; } - ret = close(client->fd); - if (ret == -1) perror("close"); + fd = client->fd; client->fd = -1; + ret = close(fd); + if (ret == -1) LOG_ERROR_N("close"); + g_thread_join(client->update); /* Environnement TLS */ + SSL_free(client->tls_fd); + client->tls_fd = NULL; + SSL_CTX_free(client->tls_ctx); client->tls_ctx = NULL; @@ -768,7 +881,7 @@ void g_db_client_stop(GDbClient *client) * * ******************************************************************************/ -int g_db_client_get_fd(GDbClient *client) +int g_hub_client_get_fd(GHubClient *client) { g_mutex_lock(&client->sending_lock); @@ -789,7 +902,7 @@ int g_db_client_get_fd(GDbClient *client) * * ******************************************************************************/ -void g_db_client_put_fd(GDbClient *client) +void g_hub_client_put_fd(GHubClient *client) { g_mutex_unlock(&client->sending_lock); @@ -808,18 +921,18 @@ void g_db_client_put_fd(GDbClient *client) * * ******************************************************************************/ -bool g_db_client_save(GDbClient *client) +bool g_hub_client_save(GHubClient *client) { bool result; /* Bilan partiel à remonter */ int sent; /* Quantité de données traitées*/ - g_db_client_get_fd(client); + g_hub_client_get_fd(client); sent = SSL_write(client->tls_fd, (uint32_t []) { htobe32(DBC_SAVE) }, sizeof(uint32_t)); result = (sent == sizeof(uint32_t)); - g_db_client_put_fd(client); + g_hub_client_put_fd(client); return result; @@ -839,21 +952,21 @@ bool g_db_client_save(GDbClient *client) * * ******************************************************************************/ -bool g_db_client_set_last_active(GDbClient *client, timestamp_t timestamp) +bool g_hub_client_set_last_active(GHubClient *client, timestamp_t timestamp) { bool result; /* Bilan partiel à remonter */ packed_buffer out_pbuf; /* Tampon d'émission */ init_packed_buffer(&out_pbuf); - g_db_client_get_fd(client); + g_hub_client_get_fd(client); result = extend_packed_buffer(&out_pbuf, (uint32_t []) { DBC_SET_LAST_ACTIVE }, sizeof(uint32_t), true); if (result) result = pack_timestamp(×tamp, &out_pbuf); - g_db_client_put_fd(client); + g_hub_client_put_fd(client); if (result) result = ssl_send_packed_buffer(&out_pbuf, client->tls_fd); diff --git a/src/analysis/db/client.h b/src/analysis/db/client.h index 23b85f8..66ca6ab 100644 --- a/src/analysis/db/client.h +++ b/src/analysis/db/client.h @@ -33,47 +33,47 @@ -#define G_TYPE_DB_CLIENT g_db_client_get_type() -#define G_DB_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_DB_CLIENT, GDbClient)) -#define G_IS_DB_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_DB_CLIENT)) -#define G_DB_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_DB_CLIENT, GDbClientClass)) -#define G_IS_DB_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_DB_CLIENT)) -#define G_DB_CLIENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_DB_CLIENT, GDbClientClass)) +#define G_TYPE_HUB_CLIENT g_hub_client_get_type() +#define G_HUB_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_HUB_CLIENT, GHubClient)) +#define G_IS_HUB_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_HUB_CLIENT)) +#define G_HUB_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_HUB_CLIENT, GHubClientClass)) +#define G_IS_HUB_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_HUB_CLIENT)) +#define G_HUB_CLIENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_HUB_CLIENT, GHubClientClass)) /* Description de client à l'écoute (instance) */ -typedef struct _GDbClient GDbClient; +typedef struct _GHubClient GHubClient; /* Description de client à l'écoute (classe) */ -typedef struct _GDbClientClass GDbClientClass; +typedef struct _GHubClientClass GHubClientClass; /* Indique le type défini pour une description de client à l'écoute. */ -GType g_db_client_get_type(void); +GType g_hub_client_get_type(void); /* Prépare un client pour une connexion à une BD. */ -GDbClient *g_db_client_new(char *, char *, const char *, const char *, GList *); +GHubClient *g_hub_client_new(const char *, GList *); /* Démarre la connexion à la base de données interne. */ -bool g_db_client_start_internal(GDbClient *); +bool g_hub_client_start_internal(GHubClient *); /* Démarre la connexion à la base de données distante. */ -bool g_db_client_start_remote(GDbClient *, const char *, unsigned short); +bool g_hub_client_start_remote(GHubClient *, const char *, const char *, bool); /* Arrête la connexion à la base de données. */ -void g_db_client_stop(GDbClient *); +void g_hub_client_stop(GHubClient *); /* Identifie le canal de communication pour envois au serveur. */ -int g_db_client_get_fd(GDbClient *); +int g_hub_client_get_fd(GHubClient *); /* Marque le canal de communication comme disponible. */ -void g_db_client_put_fd(GDbClient *); +void g_hub_client_put_fd(GHubClient *); /* Effectue une demande de sauvegarde de l'état courant. */ -bool g_db_client_save(GDbClient *); +bool g_hub_client_save(GHubClient *); /* Active les éléments en amont d'un horodatage donné. */ -bool g_db_client_set_last_active(GDbClient *, timestamp_t); +bool g_hub_client_set_last_active(GHubClient *, timestamp_t); diff --git a/src/analysis/db/keymgn.c b/src/analysis/db/keymgn.c index 4334896..d2abd7c 100644 --- a/src/analysis/db/keymgn.c +++ b/src/analysis/db/keymgn.c @@ -329,10 +329,10 @@ bool register_server_cert(const char *name, const x509_entries *entries) if (!result) goto rsc_quick_exit; - result = make_ca(working, "ca", valid, entries); + result = build_keys_and_ca(working, "ca", valid, entries); if (!result) goto rsc_quick_exit; - result = make_request(working, "server", entries); + result = build_keys_and_request(working, "server", entries); if (!result) goto rsc_quick_exit; csr = build_absolute_filename(working, "server-csr.pem"); @@ -384,7 +384,7 @@ bool make_client_sign_request(const char *name, const x509_entries *entries) result = mkpath(working); if (result) - result = make_request(working, "client", entries); + result = build_keys_and_request(working, "client", entries); if (result) store_identity(entries, true); diff --git a/src/analysis/db/keymgn.h b/src/analysis/db/keymgn.h index df4f1f5..24ada61 100644 --- a/src/analysis/db/keymgn.h +++ b/src/analysis/db/keymgn.h @@ -31,6 +31,8 @@ #include "certs.h" +#include "auth.h" + /* Charge en mémoire la définition de l'identité courante. */ void load_identity(bool, x509_entries *); diff --git a/src/analysis/db/protocol.h b/src/analysis/db/protocol.h index 311a691..1c03d62 100644 --- a/src/analysis/db/protocol.h +++ b/src/analysis/db/protocol.h @@ -29,7 +29,7 @@ /** * Version de la définition courante du protocole. */ -#define CDB_PROTOCOL_VERSION 0xc0de0003 +#define CDB_PROTOCOL_VERSION 0xc0de0004 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); + } diff --git a/src/analysis/db/server.h b/src/analysis/db/server.h index 5be7886..44aef54 100644 --- a/src/analysis/db/server.h +++ b/src/analysis/db/server.h @@ -30,35 +30,47 @@ -#define G_TYPE_DB_SERVER g_db_server_get_type() -#define G_DB_SERVER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_DB_SERVER, GDbServer)) -#define G_IS_DB_SERVER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_DB_SERVER)) -#define G_DB_SERVER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_DB_SERVER, GDbServerClass)) -#define G_IS_DB_SERVER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_DB_SERVER)) -#define G_DB_SERVER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_DB_SERVER, GDbServerClass)) +#define G_TYPE_HUB_SERVER g_hub_server_get_type() +#define G_HUB_SERVER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_HUB_SERVER, GHubServer)) +#define G_IS_HUB_SERVER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_HUB_SERVER)) +#define G_HUB_SERVER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_HUB_SERVER, GHubServerClass)) +#define G_IS_HUB_SERVER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_HUB_SERVER)) +#define G_HUB_SERVER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_HUB_SERVER, GHubServerClass)) /* Description de serveur à l'écoute (instance) */ -typedef struct _GDbServer GDbServer; +typedef struct _GHubServer GHubServer; /* Description de serveur à l'écoute (classe) */ -typedef struct _GDbServerClass GDbServerClass; +typedef struct _GHubServerClass GHubServerClass; /* Indique le type défini pour une description de serveur à l'écoute. */ -GType g_db_server_get_type(void); +GType g_hub_server_get_type(void); /* Prépare un serveur de BD pour les clients internes. */ -GDbServer *g_db_server_new_internal(const char *, char *); +GHubServer *g_hub_server_new_internal(void); /* Prépare un serveur de BD pour les clients distants. */ -GDbServer *g_db_server_new_remote(const char *); +GHubServer *g_hub_server_new_remote(const char *, const char *, bool); + +/* Bilan du lancement d'un serveur */ +typedef enum _ServerStartStatus +{ + SSS_FAILURE, /* Echec du démarrage */ + SSS_SUCCESS, /* Serveur démarré */ + SSS_ALREADY_RUNNING, /* Instance déjà en place */ + +} ServerStartStatus; /* Démarre le serveur de base de données. */ -bool g_db_server_start(GDbServer *); +ServerStartStatus g_hub_server_start(GHubServer *, int, bool); + +/* Attend l'arrête du serveur de base de données. */ +void g_hub_server_wait_for_stop(GHubServer *); /* Arrête le serveur de base de données. */ -void g_db_server_stop(GDbServer *); +void g_hub_server_stop(GHubServer *); |