/* Chrysalide - Outil d'analyse de fichiers binaires * keymgn.c - mise en place et gestion des clefs cryptographiques * * Copyright (C) 2016-2017 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 Foobar. If not, see . */ #include "keymgn.h" #include #include #include #include "../../common/extstr.h" #include "../../common/pathname.h" #include "../../common/xdg.h" #include "../../core/params.h" /* Mémorise en mémoire la définition d'une identité courante. */ static void store_identity(const x509_entries *, bool); /* Fournit le répertoire de travail pour les certifications. */ static char *get_cert_working_directory(const char *, const char *); /****************************************************************************** * * * Paramètres : client = précise la nature de l'identité à représenter. * * entries = éléments d'identité à définir en mémoire. [OUT] * * * * Description : Charge en mémoire la définition de l'identité courante. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void load_identity(bool client, x509_entries *entries) { GGenConfig *config; /* Configuration à manipuler */ const char *path; /* Accès à la configuration */ char *saved; /* Valeur sauvegardée */ char *username; /* Dénomination affichée */ char *lang; /* Langage de l'utilisateur */ size_t length; /* Taille de valeur récupérée */ size_t first; /* Indice de première majuscule*/ size_t i; /* Boucle de parcours */ memset(entries, 0, sizeof(x509_entries)); /* Chargement à partir des sauvegardes */ config = get_main_configuration(); path = (client ? MPK_IDENTITY_CLIENT_C : MPK_IDENTITY_SERVER_C); if (g_generic_config_get_value(get_main_configuration(), path, &saved)) entries->country = (saved != NULL ? strdup(saved) : NULL); path = (client ? MPK_IDENTITY_CLIENT_ST : MPK_IDENTITY_SERVER_ST); if (g_generic_config_get_value(get_main_configuration(), path, &saved)) entries->state = (saved != NULL ? strdup(saved) : NULL); path = (client ? MPK_IDENTITY_CLIENT_L : MPK_IDENTITY_SERVER_L); if (g_generic_config_get_value(get_main_configuration(), path, &saved)) entries->locality = (saved != NULL ? strdup(saved) : NULL); path = (client ? MPK_IDENTITY_CLIENT_O : MPK_IDENTITY_SERVER_O); if (g_generic_config_get_value(get_main_configuration(), path, &saved)) entries->organisation = (saved != NULL ? strdup(saved) : NULL); path = (client ? MPK_IDENTITY_CLIENT_OU : MPK_IDENTITY_SERVER_OU); if (g_generic_config_get_value(get_main_configuration(), path, &saved)) entries->organisational_unit = (saved != NULL ? strdup(saved) : NULL); path = (client ? MPK_IDENTITY_CLIENT_CN : MPK_IDENTITY_SERVER_CN); if (g_generic_config_get_value(get_main_configuration(), path, &saved)) entries->common_name = (saved != NULL ? strdup(saved) : NULL); /* Si les valeurs n'étaient pas définies... */ if (are_x509_entries_empty(entries)) { /* Identification de l'utilisateur */ username = getenv("USERNAME"); if (username == NULL) username = getenv("USER"); if (username == NULL) username = getenv("LOGNAME"); if (username != NULL) entries->common_name = strdup(username); else entries->common_name = strdup("???"); /* Identification du pays */ lang = getenv("LANG"); if (lang != NULL) { length = strlen(lang); first = length; for (i = 0; i < length; i++) { if (isupper(lang[i])) { if (first == length) first = i; } else if (first != length) { entries->country = strndup(&lang[first], i - first); break; } } } if (entries->country == NULL) entries->country = strdup("??"); } } /****************************************************************************** * * * Paramètres : entries = éléments d'identité à conserver en mémoire. * * client = précise la nature de l'identité représentée. * * * * Description : Mémorise en mémoire la définition d'une identité courante. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void store_identity(const x509_entries *entries, bool client) { GGenConfig *config; /* Configuration à manipuler */ const char *path; /* Accès à la configuration */ config = get_main_configuration(); path = (client ? MPK_IDENTITY_CLIENT_C : MPK_IDENTITY_SERVER_C); g_generic_config_set_value(config, path, entries->country); path = (client ? MPK_IDENTITY_CLIENT_ST : MPK_IDENTITY_SERVER_ST); g_generic_config_set_value(config, path, entries->state); path = (client ? MPK_IDENTITY_CLIENT_L : MPK_IDENTITY_SERVER_L); g_generic_config_set_value(config, path, entries->locality); path = (client ? MPK_IDENTITY_CLIENT_O : MPK_IDENTITY_SERVER_O); g_generic_config_set_value(config, path, entries->organisation); path = (client ? MPK_IDENTITY_CLIENT_OU : MPK_IDENTITY_SERVER_OU); g_generic_config_set_value(config, path, entries->organisational_unit); path = (client ? MPK_IDENTITY_CLIENT_CN : MPK_IDENTITY_SERVER_CN); g_generic_config_set_value(config, path, entries->common_name); } /****************************************************************************** * * * Paramètres : type = type de certificat à gérer. * * name = dénomination du serveur visé. * * * * Description : Fournit le répertoire de travail pour les certifications. * * * * Retour : Définition d'emplacement à libérer de la mémoire après usage.* * * * Remarques : - * * * ******************************************************************************/ static char *get_cert_working_directory(const char *type, const char *name) { 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); suffix = stradd(suffix, name); suffix = stradd(suffix, G_DIR_SEPARATOR_S); result = get_xdg_config_dir(suffix); free(suffix); return result; } /****************************************************************************** * * * Paramètres : entries = éléments d'identité à utiliser pour l'opération. * * * * Description : Définit les certificats utilisés pour les échanges internes. * * * * Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ bool register_standalone_certs(const x509_entries *entries) { bool result; /* Bilan de l'opération */ char *working; /* Répertoire par le client */ char *csr; /* Requête de signature */ char *cert; /* Certificat signé en sortie */ /* Certificats côtés serveur */ result = register_server_cert("standalone", entries); if (result) { /* Demande de signature */ result = make_client_sign_request("standalone", entries); /* Signature */ if (result) { working = get_cert_working_directory("clients", "standalone"); csr = build_absolute_filename(working, "client-csr.pem"); cert = build_absolute_filename(working, "client-cert.pem"); result = sign_client_request("standalone", csr, cert); free(csr); free(cert); free(working); } } return result; } /****************************************************************************** * * * Paramètres : name = dénomination du serveur visé. * * entries = éléments d'identité à utiliser pour l'opération. * * * * Description : Définit les certificats utilisés pour par un serveur. * * * * Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ bool register_server_cert(const char *name, const x509_entries *entries) { bool result; /* Bilan de l'opération */ char *suffix; /* Fin de la destination */ char *working; /* Répertoire de travail */ unsigned long valid; /* Durée de validité */ char *csr; /* Requête de signature */ char *cacert; /* Certificat d'autorité */ char *cakey; /* Clef de cette autorité */ char *cert; /* Certificat signé en sortie */ result = false; working = get_cert_working_directory("servers", name); if (working != NULL) { result = mkpath(working); if (!result) goto rsc_quick_exit; result = g_generic_config_get_value(get_main_configuration(), MPK_IDENTITY_VALIDITY, &valid); if (!result) goto rsc_quick_exit; result = make_ca(working, "ca", valid, entries); if (!result) goto rsc_quick_exit; result = make_request(working, "server", entries); if (!result) goto rsc_quick_exit; 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); if (result) store_identity(entries, false); free(csr); free(cacert); free(cakey); free(cert); rsc_quick_exit: free(working); } return result; } /****************************************************************************** * * * Paramètres : name = dénomination du serveur visé. * * entries = éléments d'identité à utiliser pour l'opération. * * * * Description : Elabore une demande de signature de certificat. * * * * Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ bool make_client_sign_request(const char *name, const x509_entries *entries) { bool result; /* Bilan de l'opération */ char *working; /* Répertoire par le client */ working = get_cert_working_directory("clients", name); result = mkpath(working); if (result) result = make_request(working, "client", entries); if (result) store_identity(entries, true); free(working); return result; } /****************************************************************************** * * * Paramètres : name = dénomination du serveur visé. * * csr = fichier contenant le certificat à signer. * * cert = fichier contenant le certificat signé. * * * * Description : Signe un certificat client pour un accès un serveur donné. * * * * Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ bool sign_client_request(const char *name, const char *csr, const char *cert) { bool result; /* Bilan de l'opération */ char *working; /* Répertoire par le serveur */ unsigned long valid; /* Durée de validité */ char *cacert; /* Certificat d'autorité */ char *cakey; /* Clef de cette autorité */ working = get_cert_working_directory("servers", name); result = g_generic_config_get_value(get_main_configuration(), MPK_IDENTITY_VALIDITY, &valid); if (!result) goto scr_exit; cacert = build_absolute_filename(working, "ca-cert.pem"); cakey = build_absolute_filename(working, "ca-key.pem"); result = sign_cert(csr, cacert, cakey, cert, valid); free(cacert); free(cakey); scr_exit: free(working); return result; } #include #include #include #include #include #include #include #include #include #include "../../common/xdg.h" /* Met en place de nouvelles clefs RSA. */ static bool generate_user_rsa_keys(const char *, const char *); /****************************************************************************** * * * Paramètres : - * * * * Description : S'assure que l'utilisateur dispose de clefs RSA. * * * * Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ bool ensure_user_has_rsa_keys(void) { bool result; /* Bilan à retourner */ char *priv; /* Chemin de la clef privée */ char *pub; /* Chemin de la clef publique */ int priv_check; /* Bilan d'une vérification #1 */ int pub_check; /* Bilan d'une vérification #2 */ result = NULL; priv = get_xdg_config_dir("chrysalide" G_DIR_SEPARATOR_S "id_rsa.priv"); pub = get_xdg_config_dir("chrysalide" G_DIR_SEPARATOR_S "id_rsa.pub"); priv_check = access(priv, R_OK); pub_check = access(pub, R_OK); result = (priv_check == 0 && pub_check == 0); if (!result) { result = generate_user_rsa_keys(priv, pub); if (!result) fprintf(stderr, _("Unable to create new user RSA key pair.\n")); } free(priv); free(pub); return result; } /****************************************************************************** * * * Paramètres : priv = chemin d'accès pour la clef privée. * * pub = chemin d'accès pour la clef publique. * * * * Description : Met en place de nouvelles clefs RSA. * * * * Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ static bool generate_user_rsa_keys(const char *priv, const char *pub) { bool result; /* Bilan à retourner */ EVP_PKEY_CTX *ctx; /* Contexte de génération */ int ret; /* Bilan d'un appel */ EVP_PKEY *pair; /* Paire de clefs RSA générée */ FILE *stream; /* Flux ouvert en écriture */ result = false; /** * Cf. https://www.openssl.org/docs/manmaster/crypto/EVP_PKEY_keygen.html */ ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL); if (ctx == NULL) goto euhrk_exit; ret = EVP_PKEY_keygen_init(ctx); if (ret != 1) goto euhrk_exit; ret = EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, RSA_USED_SIZE * 8); if (ret != 1) goto euhrk_exit; pair = NULL; ret = EVP_PKEY_keygen(ctx, &pair); if (ret != 1) goto euhrk_exit; /* Clef privée */ stream = fopen(priv, "wt"); if (stream == NULL) goto euhrk_bad_write; ret = PEM_write_PrivateKey(stream, pair, NULL, NULL, 0, NULL, NULL); if (ret != 1) goto euhrk_bad_write; fclose(stream); /* Clef publique */ stream = fopen(pub, "wt"); if (stream == NULL) goto euhrk_bad_write; ret = PEM_write_PUBKEY(stream, pair); if (ret != 1) goto euhrk_bad_write; result = true; euhrk_bad_write: fclose(stream); EVP_PKEY_free(pair); euhrk_exit: EVP_PKEY_CTX_free(ctx); return result; } /****************************************************************************** * * * Paramètres : filename = chemin d'accès à la clef à charger. * * private = nature de la clef visée. * * * * Description : Charge une clef RSA à partir d'un fichier PEM. * * * * Retour : Clef RSA ou NULL en cas de soucis. * * * * Remarques : - * * * ******************************************************************************/ RSA *load_rsa_key(const char *filename, bool private) { RSA *result; /* Clef à retourner */ FILE *stream; /* Flux ouvert en lecture */ int bits; /* Taille de la clef en bits */ result = NULL; stream = fopen(filename, "r"); if (stream == NULL) goto lrk_exit; if (private) result = PEM_read_RSAPrivateKey(stream, &result, NULL, NULL); else result = PEM_read_RSA_PUBKEY(stream, &result, NULL, NULL); fclose(stream); if (result == NULL) fprintf(stderr, _("Unable to read the RSA key from '%s'.\n"), filename); else { bits = RSA_size(result); if (bits != RSA_USED_SIZE) { fprintf(stderr, _("Wrong RSA key size for %s: expected %d, got %d.\n"), filename, RSA_USED_SIZE, bits); RSA_free(result); result = NULL; } } lrk_exit: return result; } /****************************************************************************** * * * Paramètres : key = clef RSA à utiliser. * * hash = empreinte à signer. * * sig = signature calculée. * * * * Description : Signe une empreinte MD5 à l'aide d'une clef RSA. * * * * Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ bool sign_md5_hash(RSA *key, const unsigned char *hash, unsigned char *sig) { int ret; /* Bilan de l'opération */ unsigned int siglen; /* Taille de la signature */ siglen = RSA_USED_SIZE; ret = RSA_sign(NID_md5, hash, 16, sig, &siglen, key); assert(siglen == RSA_USED_SIZE); if (ret != 1) fprintf(stderr, "Unable to sign hash (error=%lu).\n", ERR_get_error()); return (ret == 1); } /****************************************************************************** * * * Paramètres : key = clef RSA à utiliser. * * hash = empreinte à signer. * * sig = signature calculée. * * * * Description : Vérifie la signature d'une empreinte MD5 avec une clef RSA. * * * * Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ bool verify_md5_hash(RSA *key, const unsigned char *hash, unsigned char *sig) { int ret; /* Bilan de l'opération */ ret = RSA_verify(NID_md5, hash, 16, sig, RSA_USED_SIZE, key); return (ret == 1); }