diff options
Diffstat (limited to 'src/analysis/db')
| -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 | 
14 files changed, 1621 insertions, 505 deletions
| 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 *); | 
