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