diff options
author | Cyrille Bagard <nocbos@gmail.com> | 2019-08-29 21:43:47 (GMT) |
---|---|---|
committer | Cyrille Bagard <nocbos@gmail.com> | 2019-08-29 21:43:47 (GMT) |
commit | fa40856e942a7e1bd1cb2729645182c1fa717468 (patch) | |
tree | 954db169d2b734e661d904e502cd1c803f51c6ea /src/analysis/db/client.c | |
parent | 7f973e015eb59b626edc584a19a1ad3ffddf4867 (diff) |
Defined a new way to launch updates share servers.
Diffstat (limited to 'src/analysis/db/client.c')
-rw-r--r-- | src/analysis/db/client.c | 373 |
1 files changed, 243 insertions, 130 deletions
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); |