/* 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 . */ #include "auth.h" #include #include #include #include #include #include #include #include #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 : - * * * * Description : Détermine la désignation par défaut de l'usager. * * * * Retour : Nom déterminé à libérer de la mémoire. * * * * Remarques : - * * * ******************************************************************************/ char *get_default_username(void) { char *result; /* Désignation à retourner */ uid_t uid; /* Identifiant d'utilisateur */ struct passwd *pw; /* Indications sur l'usager */ uid = geteuid(); pw = getpwuid(uid); if (pw != NULL) result = strdup(pw->pw_name); else result = strdup("anonymous"); 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 */ working = get_db_working_directory("clients", NULL, NULL, NULL); result = mkpath(working); if (result) { if (entries->common_name == NULL) { entries->common_name = get_default_username(); log_variadic_message(LMT_WARNING, _("Replaced the empty identity common name with '%s'"), entries->common_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; }