NOM¶
moteur - ENGINE module de support cryptographique
SYNOPSIS¶
#include <openssl/engine.h>
ENGINE *ENGINE_get_first(void);
ENGINE *ENGINE_get_last(void);
ENGINE *ENGINE_get_next(ENGINE *e);
ENGINE *ENGINE_get_prev(ENGINE *e);
int ENGINE_add(ENGINE *e);
int ENGINE_remove(ENGINE *e);
ENGINE *ENGINE_by_id(const char *id);
int ENGINE_init(ENGINE *e);
int ENGINE_finish(ENGINE *e);
void ENGINE_load_openssl(void);
void ENGINE_load_dynamic(void);
#ifndef OPENSSL_NO_STATIC_ENGINE
void ENGINE_load_4758cca(void);
void ENGINE_load_aep(void);
void ENGINE_load_atalla(void);
void ENGINE_load_chil(void);
void ENGINE_load_cswift(void);
void ENGINE_load_gmp(void);
void ENGINE_load_nuron(void);
void ENGINE_load_sureware(void);
void ENGINE_load_ubsec(void);
#endif
void ENGINE_load_cryptodev(void);
void ENGINE_load_builtin_engines(void);
void ENGINE_cleanup(void);
ENGINE *ENGINE_get_default_RSA(void);
ENGINE *ENGINE_get_default_DSA(void);
ENGINE *ENGINE_get_default_ECDH(void);
ENGINE *ENGINE_get_default_ECDSA(void);
ENGINE *ENGINE_get_default_DH(void);
ENGINE *ENGINE_get_default_RAND(void);
ENGINE *ENGINE_get_cipher_engine(int nid);
ENGINE *ENGINE_get_digest_engine(int nid);
int ENGINE_set_default_RSA(ENGINE *e);
int ENGINE_set_default_DSA(ENGINE *e);
int ENGINE_set_default_ECDH(ENGINE *e);
int ENGINE_set_default_ECDSA(ENGINE *e);
int ENGINE_set_default_DH(ENGINE *e);
int ENGINE_set_default_RAND(ENGINE *e);
int ENGINE_set_default_ciphers(ENGINE *e);
int ENGINE_set_default_digests(ENGINE *e);
int ENGINE_set_default_string(ENGINE *e, const char *list);
int ENGINE_set_default(ENGINE *e, unsigned int flags);
unsigned int ENGINE_get_table_flags(void);
void ENGINE_set_table_flags(unsigned int flags);
int ENGINE_register_RSA(ENGINE *e);
void ENGINE_unregister_RSA(ENGINE *e);
void ENGINE_register_all_RSA(void);
int ENGINE_register_DSA(ENGINE *e);
void ENGINE_unregister_DSA(ENGINE *e);
void ENGINE_register_all_DSA(void);
int ENGINE_register_ECDH(ENGINE *e);
void ENGINE_unregister_ECDH(ENGINE *e);
void ENGINE_register_all_ECDH(void);
int ENGINE_register_ECDSA(ENGINE *e);
void ENGINE_unregister_ECDSA(ENGINE *e);
void ENGINE_register_all_ECDSA(void);
int ENGINE_register_DH(ENGINE *e);
void ENGINE_unregister_DH(ENGINE *e);
void ENGINE_register_all_DH(void);
int ENGINE_register_RAND(ENGINE *e);
void ENGINE_unregister_RAND(ENGINE *e);
void ENGINE_register_all_RAND(void);
int ENGINE_register_STORE(ENGINE *e);
void ENGINE_unregister_STORE(ENGINE *e);
void ENGINE_register_all_STORE(void);
int ENGINE_register_ciphers(ENGINE *e);
void ENGINE_unregister_ciphers(ENGINE *e);
void ENGINE_register_all_ciphers(void);
int ENGINE_register_digests(ENGINE *e);
void ENGINE_unregister_digests(ENGINE *e);
void ENGINE_register_all_digests(void);
int ENGINE_register_complete(ENGINE *e);
int ENGINE_register_all_complete(void);
int ENGINE_ctrl(ENGINE *e, int cmd, long i, void *p, void (*f)(void));
int ENGINE_cmd_is_executable(ENGINE *e, int cmd);
int ENGINE_ctrl_cmd(ENGINE *e, const char *cmd_name,
long i, void *p, void (*f)(void), int cmd_optional);
int ENGINE_ctrl_cmd_string(ENGINE *e, const char *cmd_name, const char *arg,
int cmd_optional);
int ENGINE_set_ex_data(ENGINE *e, int idx, void *arg);
void *ENGINE_get_ex_data(const ENGINE *e, int idx);
int ENGINE_get_ex_new_index(long argl, void *argp, CRYPTO_EX_new *new_func,
CRYPTO_EX_dup *dup_func, CRYPTO_EX_free *free_func);
ENGINE *ENGINE_new(void);
int ENGINE_free(ENGINE *e);
int ENGINE_up_ref(ENGINE *e);
int ENGINE_set_id(ENGINE *e, const char *id);
int ENGINE_set_name(ENGINE *e, const char *name);
int ENGINE_set_RSA(ENGINE *e, const RSA_METHOD *rsa_meth);
int ENGINE_set_DSA(ENGINE *e, const DSA_METHOD *dsa_meth);
int ENGINE_set_ECDH(ENGINE *e, const ECDH_METHOD *dh_meth);
int ENGINE_set_ECDSA(ENGINE *e, const ECDSA_METHOD *dh_meth);
int ENGINE_set_DH(ENGINE *e, const DH_METHOD *dh_meth);
int ENGINE_set_RAND(ENGINE *e, const RAND_METHOD *rand_meth);
int ENGINE_set_STORE(ENGINE *e, const STORE_METHOD *rand_meth);
int ENGINE_set_destroy_function(ENGINE *e, ENGINE_GEN_INT_FUNC_PTR destroy_f);
int ENGINE_set_init_function(ENGINE *e, ENGINE_GEN_INT_FUNC_PTR init_f);
int ENGINE_set_finish_function(ENGINE *e, ENGINE_GEN_INT_FUNC_PTR finish_f);
int ENGINE_set_ctrl_function(ENGINE *e, ENGINE_CTRL_FUNC_PTR ctrl_f);
int ENGINE_set_load_privkey_function(ENGINE *e, ENGINE_LOAD_KEY_PTR loadpriv_f);
int ENGINE_set_load_pubkey_function(ENGINE *e, ENGINE_LOAD_KEY_PTR loadpub_f);
int ENGINE_set_ciphers(ENGINE *e, ENGINE_CIPHERS_PTR f);
int ENGINE_set_digests(ENGINE *e, ENGINE_DIGESTS_PTR f);
int ENGINE_set_flags(ENGINE *e, int flags);
int ENGINE_set_cmd_defns(ENGINE *e, const ENGINE_CMD_DEFN *defns);
const char *ENGINE_get_id(const ENGINE *e);
const char *ENGINE_get_name(const ENGINE *e);
const RSA_METHOD *ENGINE_get_RSA(const ENGINE *e);
const DSA_METHOD *ENGINE_get_DSA(const ENGINE *e);
const ECDH_METHOD *ENGINE_get_ECDH(const ENGINE *e);
const ECDSA_METHOD *ENGINE_get_ECDSA(const ENGINE *e);
const DH_METHOD *ENGINE_get_DH(const ENGINE *e);
const RAND_METHOD *ENGINE_get_RAND(const ENGINE *e);
const STORE_METHOD *ENGINE_get_STORE(const ENGINE *e);
ENGINE_GEN_INT_FUNC_PTR ENGINE_get_destroy_function(const ENGINE *e);
ENGINE_GEN_INT_FUNC_PTR ENGINE_get_init_function(const ENGINE *e);
ENGINE_GEN_INT_FUNC_PTR ENGINE_get_finish_function(const ENGINE *e);
ENGINE_CTRL_FUNC_PTR ENGINE_get_ctrl_function(const ENGINE *e);
ENGINE_LOAD_KEY_PTR ENGINE_get_load_privkey_function(const ENGINE *e);
ENGINE_LOAD_KEY_PTR ENGINE_get_load_pubkey_function(const ENGINE *e);
ENGINE_CIPHERS_PTR ENGINE_get_ciphers(const ENGINE *e);
ENGINE_DIGESTS_PTR ENGINE_get_digests(const ENGINE *e);
const EVP_CIPHER *ENGINE_get_cipher(ENGINE *e, int nid);
const EVP_MD *ENGINE_get_digest(ENGINE *e, int nid);
int ENGINE_get_flags(const ENGINE *e);
const ENGINE_CMD_DEFN *ENGINE_get_cmd_defns(const ENGINE *e);
EVP_PKEY *ENGINE_load_private_key(ENGINE *e, const char *key_id,
UI_METHOD *ui_method, void *callback_data);
EVP_PKEY *ENGINE_load_public_key(ENGINE *e, const char *key_id,
UI_METHOD *ui_method, void *callback_data);
void ENGINE_add_conf_module(void);
DESCRIPTION¶
Ces fonctions créent, manipulent et utilisent des modules
cryptographiques sous la forme d'objets
ENGINE. Ces objets servent de
conteneurs pour les implémentations d'algorithmes cryptographiques, et
ils gèrent un mécanisme de comptage de référence
qui les autorisent à être chargés de façon
dynamique dans l'application exécutée et en dehors.
La fonctionnalité cryptographique qui peut être fournie par
l'implémentation d'un
ENGINE inclut les abstractions
suivantes :
RSA_METHOD - pour fournir une implémentation RSA alternative
DSA_METHOD, DH_METHOD, RAND_METHOD, ECDH_METHOD, ECDSA_METHOD,
STORE_METHOD - identique pour d'autres API d'OpenSSL
EVP_CIPHER - potentiellement plusieurs algorithmes de chiffrement (indexé par 'nid')
EVP_DIGEST - potentiellement plusieurs algorithmes de hachage (indexé par 'nid')
key-loading - chargement des clés EVP_PKEY publiques/privées
Référence de calculs et traitements¶
À cause de la nature modulaire de l'API ENGINE, les pointeurs vers les
ENGINE doivent être traités comme des références
abstraites vers des ressources — c'est-à-dire pas
seulement comme des pointeurs, mais aussi comme des références
vers l'objet ENGINE sous-jacent. C'est-à-dire que l'on doit obtenir une
nouvelle référence lors de la copie d'un pointeur vers un ENGINE
si la copie doit être utilisée (et libérée) de
façon indépendante.
Les objets ENGINE ont deux niveaux de comptage de références pour
correspondre à la façon dont les objets sont utilisés.
À plus bas niveau, chaque pointeur ENGINE est intrinsèquement
une référence
structurelle - une référence
structurelle est nécessaire pour utiliser toute valeur de pointeur, car
ce type de référence garantit que la structure ne peut pas
être désallouée avant que sa référence ne
soit libérée.
Mais, une référence structurelle n'offre aucune garantie que
l'ENGINE soit initialisé et capable d'utiliser n'importe laquelle de
ses implémentations cryptographiques. En effet, il est fort probable
que la plupart des ENGINE ne puissent pas s'initialiser dans des
environnements typiques, car les ENGINES sont généralement
utilisés pour gérer du matériel spécialisé.
Pour utiliser une fonctionnalité d'ENGINE, il est nécessaire
d'avoir une référence
fonctionnelle. Cette
référence peut être considérée comme une
forme spécialisée de référence structurelle, car
chaque référence fonctionnelle contient de façon
implicite une référence structurelle. Par contre, pour
éviter d'avoir des programmes durs à déboguer, il est
recommandé de traiter ces deux types de référence
indépendamment l'une de l'autre. Si une référence
fonctionnelle est disponible pour un ENGINE, il est garanti que cet ENGINE a
été initialisé et est prêt à faire des
opérations cryptographiques et restera non-initialisable
jusqu’à la libération de la référence.
Structural references
Ce type de référence de base est utilisé pour instancier de
nouveaux ENGINE, itérer sur les listes liées d'ENGINE
chargés d'OpenSSL, lire les informations d'un ENGINE, etc. Une
référence structurelle est essentiellement suffisante pour des
requêtes ou pour manipuler les données d'implémentation
d'un ENGINE au lieu d'utiliser ses fonctionnalités.
La fonction
ENGINE_new() renvoie une référence structurelle
vers un objet ENGINE vide. Il y a d'autres fonctions d'API dans ENGINE qui
renvoient une référence structurelle comme :
ENGINE_by_id(),
ENGINE_get_first(),
ENGINE_get_last(),
ENGINE_get_next(),
ENGINE_get_prev(). Toute
référence structurelle devrait être libérée
par un appel correspondant à la fonction
ENGINE_free()
— l'objet ENGINE lui-même ne sera nettoyé et
désalloué que lorsque la dernière référence
structurelle sera libérée.
On doit aussi remarquer que beaucoup d'appels de fonctions d'API d'ENGINE qui
acceptent une référence structurelle obtiendront de façon
interne une autre référence — en
général cela ce produira lorsque l'ENGINE sera nécessaire
à OpenSSL après le retour de la fonction. Exemple : la
fonction d'ajout d'un nouvel ENGINE à la liste interne d'OpenSSL est
ENGINE_add() — si cette actionse termine avec
succès, alors OpenSSL aura stocké une nouvelle
référence structurelle en interne, aussi l'appelant est toujours
responsable de la libération de ses propres références
avec
ENGINE_free() quand les fonctions n'en ont plus besoin. D'une
façon similaire, certaines fonctions libéreront automatiquement
la référence structurelle qui leur est donnée si une
partie du travail de la fonction est de faire cela. Exemple : les
fonctions
ENGINE_get_next() et
ENGINE_get_prev() sont
utilisées pour itérer à travers la liste interne d'ENGINE
— elles renverront une nouvelle référence
structurelle au prochain (ou précédent) ENGINE dans la liste ou
NULL si elles sont à la fin (ou au début) de la liste, mais,
dans tous les cas, la référence structurelle passée
à la fonction est libérée au nom de l'appelant.
Pour éclaircir la façon dont une fonction traite les
références, on doit toujours consulter la documentation en
utilisant la page man de cette fonction, ou à défaut
l’en-tête openssl/engine.h qui contient des indices.
Références fonctionnelles
Comme mentionné, les références fonctionnelles existent
lorsque la fonctionnalité cryptographique d'un ENGINE doit être
disponible. Une référence fonctionnelle peut être obtenue
de plusieurs façons : d'une référence structurelle
pré-existante demandée par l'ENGINE, ou en demandant à
l'ENGINE par défaut d'OpenSSL pour une tâche cryptographique
donnée.
Pour obtenir une référence fonctionnelle à partir d'une
référence structurelle, appelez la fonction
ENGINE_init(). Cela renvoie 0 si l'ENGINE n'était pas
déjà opérationnel et ne pouvait donc pas être
initialisé (ex : le manque de pilotes dans le système,
pas de matériel spécifique attaché, etc), sinon
elle renverra autre chose que 0 pour indiquer que l'ENGINE est maintenant
opérationnel et a alloué une référence
fonctionnelle à l'ENGINE. Toutes les références
fonctionnelles sont libérées en appelant
ENGINE_finish()
(ce qui supprime les références structurelles implicites aussi).
La deuxième façon de récupérer des
références fonctionnelles est de demander à OpenSSL une
implémentation par défaut pour la tâche voulue,
ex : avec
ENGINE_get_default_RSA(),
ENGINE_get_default_cipher_engine(), etc. Celles-ci sont
expliquées dans la prochaine partie, mais elles ne sont d'habitude pas
requises par les programmeurs d'application car elles sont utilisées
automatiquement quand les spécificités de types de l'algorithme
dans OpenSSL sont créées et utilisées, comme RSA, DSA,
EVP_CIPHER_CTX, etc.
Implémentations par défaut¶
Pour chaque abstraction gérée, le code d'ENGINE maintient une
table d'état de contrôle interne dans laquelle les
implémentations sont disponibles pour une abstraction donnée et
qui devrait être utilisée par défaut. Ces
implémentations sont enregistrées dans les tables et
indexées par une valeur « nid », parce que
les abstractions comme EVP_CIPHER et EVP_DIGEST gèrent
énormément d'algorithmes et de modes distincts, et les ENGINE
peuvent arbitrairement tous les prendre en charge. Dans le cas d'autres
abstractions comme RSA, DSA, etc, il n'y a qu'un seul
« algorithme » donc toutes les
implémentations sont enregistrées de façon implicite en
utilisant le même index « nid ».
Quand une requête d'un ENGINE par défaut pour une
abstraction/algorithme/mode est faite (par ex. en appelant
RSA_new_method(NULL)), un appel « get_default »
peut être fait au sous-système de l'ENGINE pour traiter
l'état de la table correspondante et renvoyer une
référence fonctionnelle vers un ENGINE initialisé dont
l'implémentation devrait être utilisée. Si aucun ENGINE
ne doit (ou ne peut) être utilisé, elle renverra NULL et
l'appelant opérera avec un identificateur d'ENGINE NULL
— cela correspond à utiliser l'implémentation
conventionnelle d'un programme. Dans ce cas, OpenSSL fonctionnera de la
même façon qu'avant que l'API ENGINE n'existe.
Chaque table d'état a un drapeau pour indiquer si elle a traité
cette requête « get_default » depuis que la
table a été modifiée, car pour traiter cette question
elle doit itérer à travers tous les ENGINE enregistrés
dans la table en essayant de les initialiser les uns après les autres,
au cas où l'un d'entre eux soit opérationnel. Si elle renvoie
une référence fonctionnelle vers un ENGINE, elle mettra en cache
une autre référence pour accélérer le processus en
cas de futures requêtes (plus besoin d'avoir à itérer sur
toute la table). De même, elle mettra en cache une réponse NULL
si aucun ENGINE n'est disponible pour que les futures requêtes ne
fassent pas la même itération, sauf si l'état de la table
change. Ce comportement peut aussi être changé ; si le
drapeau ENGINE_TABLE_FLAG_NOINIT est défini (en utilisant
ENGINE_set_table_flags()), il n'y aura pas de tentatives
d'initialisation, et la seule façon pour une table d'état de
renvoyer à un ENGINE non NULL pour la requête
« get_default » sera celle qui sera
indiquée de façon explicite dans la table. Exemple :
ENGINE_set_default_RSA() fait la même chose qu'
ENGINE_register_RSA() sauf qu'elle définit aussi le cache de
réponses de la table pour la requête
« get_default ». Dans ce cas, les abstractions
comme EVP_CIPHER, où les implémentations sont indexées
par un « nid », ces drapeaux et réponses
cachés sont différents des valeurs
« nid ».
Exigences de l'application¶
Cette partie expliquera les choses de base qu'un programmeur d'applications doit
connaître pour rendre disponibles les éléments les plus
utiles de la fonctionnalité d'ENGINE à l'utilisateur. La
première chose à considérer est si le programmeur veut
fournir un module alternatif d'ENGINE disponible à l'application et
à l'utilisateur. OpenSSL maintient une liste liée interne
d'ENGINE « visibles » qu'il a besoin
d'opérer — au démarrage, cette liste est vide et
d'ailleurs si une application n'utilise pas d'appel de l'API d'ENGINE et
qu'elle utilise un lien statique envers openssl, alors le binaire de
l'application résultante ne contiendra aucun code d'ENGINE alternatif.
La première considération est donc de savoir si toute
implémentation d'ENGINE doit être rendue visible à
OpenSSL — cela est contrôlé en appelant les
différentes fonctions de « chargement »,
exemple :
/* Rendre l'ENGINE S<« dynamique »> disponible */
void ENGINE_load_dynamic(void);
/* Rendre le support d'accélération matériel CryptoSwift disponible */
void ENGINE_load_cswift(void);
/* Rendre le support de matériel S<« CHIL »> de nCipher disponible */
void ENGINE_load_chil(void);
...
/* Rendre toutes les implémentations du paquet OpenSSL disponibles. */
void ENGINE_load_builtin_engines(void);
Après avoir appelé n'importe laquelle de ces fonctions, les objets
ENGINE ont été alloués de façon dynamique,
peuplés avec ces implémentations et liés à la
liste chaînée interne d'OpenSSL. À partir de ce moment il
est nécessaire de mentionner une fonction de l'API importante ;
void ENGINE_cleanup(void);
Si aucune fonction d'API ENGINE n'est appelée dans une application, alors
il n'y a pas de fuite mémoire dont il faut s'inquiéter dans les
fonctionnalités d'ENGINE ; néanmoins, si n'importe quel
ENGINE est chargé, même s'il n'est jamais enregistré ou
utilisé, il est nécessaire d'utiliser la fonction
ENGINE_cleanup() pour le nettoyer de façon correcte avant la
fermeture du programme, si l'utilisateur veut éviter les fuites
mémoires. Ce mécanisme utilise une table enregistrant les
rétroactions internes pour qu'une fonctionnalité d'API ENGINE,
qui sait qu'elle a besoin d'être nettoyée, puisse enregistrer
les détails du nettoyage à appeler pendant l'appel
deENGINE_cleanup(). Cette approche permet à
ENGINE_cleanup() de nettoyer après n'importe quelle utilisation
par un programme d'une fonctionnalité d'ENGINE, mais cela ne
crée pas de lien de dépendance à toutes les
fonctionnalités possibles d'ENGINE — seules les
rétroactions de nettoyage nécessaires aux fonctionnalités
utilisées, seront requises par le lieur.
Le fait que les ENGINES sont rendus visibles à OpenSSL (et de ce fait
sont liés au programme et chargés en mémoire lors de
l'exécution) ne veut pas dire qu'ils sont
« enregistrés » ou appelés et
utilisés par OpenSSL automatiquement — ce comportement
est quelque chose qui doit être contrôlé par
l'application. Certaines applications auront besoin d'autoriser l'utilisateur
à spécifier exactement quel ENGINE il voudra utiliser si il y en
a un qui doit être utilisé. D'autres préféreront
tout charger pour que OpenSSL utilise automatiquement le premier ENGINE qui
peut s'initialiser correctement — c'est-à-dire, qui
puisse correspondre à une accélération matérielle
attachée à la machine, ou quelque chose comme cela. Il y a
probablement énormément d'autres façons par lesquelles
l'application peut préférer gérer les choses, nous
illustrerons donc simplement les conséquences telles qu'elles
s'appliquent à des cas simples et laisserons les développeurs
considérer ces cas et le code source de fonctions utilitaires d'OpenSSL
comme guide.
Utiliser une implémentation spécifique d'ENGINE
Ici, nous allons supposer qu'une application a été
configurée par son utilisateur ou administrateur pour utiliser l'ENGINE
« ACME » s'il est disponible dans la version
d'OpenSSL avec laquelle l'application a été compilée.
S'il est disponible, il devra être utilisé par défaut
pour tous les chiffrements RSA, DSA et les opérations par chiffrement
symétrique, sinon OpenSSL devrait utiliser ses fonctionnalités
internes comme d'habitude. Le code suivant illustre comment faire
cela :
ENGINE *e;
const char *engine_id = "ACME";
ENGINE_load_builtin_engines();
e = ENGINE_by_id(engine_id);
if(!e)
/* le moteur n'est pas disponible */
return;
if(!ENGINE_init(e)) {
/* le moteur n'a pas pu être initialisé, libérer S<« e »> */
ENGINE_free(e);
return;
}
if(!ENGINE_set_default_RSA(e))
/* Ceci devrait uniquement se produire quand S<« e »> ne peut être initialisé, mais la déclaration précédente suggère qu'il l'a été. */
abort();
ENGINE_set_default_DSA(e);
ENGINE_set_default_ciphers(e);
/* Libère la référence fonctionnelle d'ENGINE_init() */
ENGINE_finish(e);
/* Libère la référence structurelle d'ENGINE_by_id() */
ENGINE_free(e);
Utiliser automatiquement les implémentations intégrées
d'ENGINE
Ici, nous allons supposer que l'on veut charger et enregistrer toutes les
implémentations d'ENGINE fournies avec OpenSSL, de façon
à ce que n'importe quel algorithme de cryptographie qui est
utilisé par OpenSSL — s'il y a un ENGINE qui
l'implémente et peut être initialisé —
alors il sera utilisé. Le code suivant montre un exemple
d'utilisation :
/* Charger et rendre visible tous les paquets ENGINE dans la mémoire. */
ENGINE_load_builtin_engines();
/* Enregistrer tous les paquets pour tous les algorithmes qu'ils implémentent collectivement */
ENGINE_register_all_complete();
Cela est tout ce qui est requis. Par exemple, la prochaine fois qu'OpenSSL
essaye de mettre en place une clé RSA, n'importe quel ENGINE fourni qui
implémente RSA_METHOD sera passé à
ENGINE_init()
et si l'un d'entre eux réussit, cet ENGINE sera choisi par
défaut pour l'utilisation de RSA.
Support de configuration avancé¶
Il y a un mécanisme géré par le framework de l'ENGINE qui
autorise chaque implémentation d'ENGINE de définir un nombre
arbitraire d'ensembles de « commandes » de
configuration, de les révéler à OpenSSL et toute
application basée sur OpenSSL. Ce mécanisme est
entièrement basé sur l'utilisation de couple noms-valeurs et
suppose que l'entrée est au format ASCII (pas d'unicode ou d'UTF pour
l'instant !), c'est donc idéal si l'application veut fournir une
possibilité de fournir, d'une façon transparente aux
utilisateurs, des « directives » de configuration
arbitraires directement à ces ENGINE. Il permet aussi à
l'application de demander de façon dynamique à
l'implémentation de l'ENGINE des noms, descriptions et drapeaux
d'entrée des « commandes de
contrôle » disponibles, ce qui fournit un programme plus
flexible. Or, si l'utilisateur s'attend à connaitre quel ENGINE il
utilise (dans le cas de matériel spécialisé, cela va sans
dire) alors l'application peut ne pas s'intéresser à la
découverte des commandes de contrôle gérées et
plus simplement préférer passer des réglages directement
à l'ENGINE tels qu'ils sont fournis par l'utilisateur.
Avant d'illustrer les commandes de contrôle, il est raisonnable de
mentionner ce pour quoi elles sont utilisées. De façon
générale, il y a deux utilisations de commandes de
contrôle ; la première consiste à fournir les
détails nécessaires à l'implémentation (qui peut
ne rien connaître du système hôte) pour que
l'initialisation se fasse. Cela peut inclure n'importe quel chemin de pilote
ou de fichier config qui doit être chargé, des adresses
réseau requises, des identifiants de cartes à puce, des mots de
passe pour initialiser des dispositifs sécurisés, des
informations de connexion, etc. Cette classe de commandes a
généralement besoin d'être passée à un
ENGINE
avant d'essayer de l'initialiser, c'est-à-dire avant
d'appeler
ENGINE_init(). L'autre classe de commande consiste en des
réglages ou des opérations qui modifient certains comportements
ou déclenchent la réalisation de certaines opérations, et
ces commandes peuvent fonctionner avant ou après
ENGINE_init(),
ou dans certains cas avant et après. L'implémentation d'ENGINE
doit fournir des indications de cela dans la description des commandes de
contrôle intégrées et/ou dans la documentation externe
finale.
Envoyer des commandes de contrôle à un ENGINE
Illustrons cela grâce à un exemple : une fonction pour
laquelle l'appelant fournit le nom de l'ENGINE qu'il veut utiliser, une table
de paires de chaînes de caractères à utiliser avant
l'initialisation, et d'autres tables après l'initialisation. Notez que
les paires de chaînes de caractères utilisées pour les
commandes de contrôles consistent en un
« nom » de commande suivit du
« paramètre » de la commande
— le paramètre pourrait être NULL dans certains
cas mais le nom ne peut pas l'être. Cette fonction devrait initialiser
l'ENGINE (en envoyant les « pré » commandes
avant et les « post » commandes après) et
le régler comme défaut pour tout sauf RAND, puis renvoyer un
booléen pour un succès ou un échec.
int generic_load_engine_fn(const char *engine_id,
const char **pre_cmds, int pre_num,
const char **post_cmds, int post_num)
{
ENGINE *e = ENGINE_by_id(engine_id);
if(!e) return 0;
while(pre_num--) {
if(!ENGINE_ctrl_cmd_string(e, pre_cmds[0], pre_cmds[1], 0)) {
fprintf(stderr, "Failed command (%s - %s:%s)\n", engine_id,
pre_cmds[0], pre_cmds[1] ? S<pre_cmds[1] :> "(NULL)");
ENGINE_free(e);
return 0;
}
pre_cmds += 2;
}
if(!ENGINE_init(e)) {
fprintf(stderr, "Failed initialisation\n");
ENGINE_free(e);
return 0;
}
/* ENGINE_init() a renvoyé une référence fonctionnelle, il faut donc libérer la référence structurelle d'ENGINE_by_id(). */
ENGINE_free(e);
while(post_num--) {
if(!ENGINE_ctrl_cmd_string(e, post_cmds[0], post_cmds[1], 0)) {
fprintf(stderr, "Failed command (%s - %s:%s)\n", engine_id,
post_cmds[0], post_cmds[1] ? S<post_cmds[1] :> "(NULL)");
ENGINE_finish(e);
return 0;
}
post_cmds += 2;
}
ENGINE_set_default(e, ENGINE_METHOD_ALL & ~ENGINE_METHOD_RAND);
/* Succès */
return 1;
}
Notez qu'
ENGINE_ctrl_cmd_string() accepte un argument booléen qui
peut assouplir les sémantiques de la fonction — s'il est
différent de 0, elle ne renverra un échec que si l'ENGINE prend
en charge le nom de la commande mais a échoué lors de son
exécution ; si l'ENGINE ne prend pas en charge le nom de la
commande, elle renverra simplement succès sans rien faire. Dans ce cas,
on suppose que l'utilisateur apporte des commandes spécifiques à
l'ENGINE donné, on règle donc cela à FALSE.
Découverte de commandes de contrôle gérées
Il est possible de découvrir les noms, id numériques, descriptions
et paramètres d'entrée des commandes de contrôle prises
en charge par un ENGINE au moment de l'exécution en utilisant une
référence structurelle. Notez que certaines commandes de
contrôle définies dans OpenSSL intercepteront et géreront
ces appels au nom d'ENGINE, c'est-à-dire le gestionnaire
ctrl()
d'ENGINE n'est pas utilisé pour les commandes de contrôle.
openssl/engine.h définit un index, ENGINE_CMD_BASE, à partir
duquel toutes les commandes de contrôle implémentées par
ENGINE doivent être numérotées. Toute valeur de commande
plus basse que ce symbole est considérée comme
« générique » et est
gérée directement par les routines noyau d'OpenSSL.
C'est en utilisant ces contrôles
« essentiels » que l'on peut découvrir les
commandes de contrôle implémentées par un ENGINE
donné, plus spécifiquement ces commandes :
#define ENGINE_HAS_CTRL_FUNCTION 10
#define ENGINE_CTRL_GET_FIRST_CMD_TYPE 11
#define ENGINE_CTRL_GET_NEXT_CMD_TYPE 12
#define ENGINE_CTRL_GET_CMD_FROM_NAME 13
#define ENGINE_CTRL_GET_NAME_LEN_FROM_CMD 14
#define ENGINE_CTRL_GET_NAME_FROM_CMD 15
#define ENGINE_CTRL_GET_DESC_LEN_FROM_CMD 16
#define ENGINE_CTRL_GET_DESC_FROM_CMD 17
#define ENGINE_CTRL_GET_CMD_FLAGS 18
Alors que ces commandes sont traitées de façon automatique par le
code du framework d'OpenSSL, elles utilisent diverses propriétés
exposées par chaque ENGINE pour traiter ces requêtes. Un ENGINE
a 3 propriétés qu'il expose qui peuvent affecter comment
celles-ci se comportent ; il peut fournir un contrôleur
ctrl(), il peut spécifier ENGINE_FLAGS_MANUAL_CMD_CTRL dans les
drapeaux de l'ENGINE, et il peut exposer un tableau de descriptions de
commandes de contrôle. Si un ENGINE spécifie le drapeau
ENGINE_FLAGS_MANUAL_CMD_CTRL, alors les commandes essentielles de
contrôle seront simplement passées directement au gestionnaire
ctrl() de l'ENGINE (et de ce fait, il doit y en avoir un de fourni),
c'est donc le rôle de l'ENGINE de répondre à ces
commandes « découvertes ». Si un drapeau
n'est pas déterminé, alors le code du framework d'OpenSSL
fonctionnera avec les règles suivantes :
si aucun gestionnaire ctrl() n'est S<fourni ;>
ENGINE_HAS_CTRL_FUNCTION renvoie FALSE (zero),
toutes les autres commandes échouent.
si un gestionnaire ctrl() a été fourni mais aucun tableau de commandes de contrôle n'a été S<fourni ;>
ENGINE_HAS_CTRL_FUNCTION renvoie TRUE,
toutes les autres commandes échouent.
si un gestionnaire ctrl() et un tableau de commandes ont été S<fournis ;>
ENGINE_HAS_CTRL_FUNCTION renvoie TRUE,
toutes les autres commandes continuent leurs traitements ...
Si le tableau de commandes de contrôle d'ENGINE est vide alors toutes les
autres commandes échoueront, sinon ENGINE_CTRL_GET_FIRST_CMD_TYPE
renvoie l'identifiant de la première commande prise en charge par
l'ENGINE, ENGINE_GET_NEXT_CMD_TYPE prend l'identifiant d'une commande prise en
charge par l'ENGINE et renvoie le prochain identifiant de commande, ou
échoue s'il n'y en a plus, ENGINE_CMD_FROM_NAME prend un nom sous forme
de chaîne de caractères pour une commande et renvoie
l'identifiant correspondant ou échoue si la commande correspondante
n'existe pas. Les commandes restantes prennent un identifiant de commande et
renvoient les propriétés de la commande correspondante. Toutes,
sauf ENGINE_CTRL_GET_FLAGS, renvoient la longueur de la chaîne de
caractères du nom de la commande ou de la description, ou remplissent
un tampon de caractères fourni avec la copie du nom ou de la
description de la commande. ENGINE_CTRL_GET_FLAGS renvoie un masque OR bit
à bit ayant les valeurs possibles suivantes :
#define ENGINE_CMD_FLAG_NUMERIC (unsigned int)0x0001
#define ENGINE_CMD_FLAG_STRING (unsigned int)0x0002
#define ENGINE_CMD_FLAG_NO_INPUT (unsigned int)0x0004
#define ENGINE_CMD_FLAG_INTERNAL (unsigned int)0x0008
Si le drapeau ENGINE_CMD_FLAG_INTERNAL est défini, alors n'importe quels
autres drapeaux sont purement informationnels pour l'appelant
— ce drapeau empêchera la commande d'être
exécutée par les fonctions haut niveau d'un ENGINE comme
ENGINE_ctrl_cmd_string(). Les commandes
« INTERNES » ne sont pas supposées
être exposées à des configurations basées sur du
texte par les applications, administrations, utilisateurs, etc.
Celles-ci peuvent gérer des opérations arbitraires à
l’aide d'
ENGINE_ctrl(), y compris le passage de ou vers les
données des commandes de contrôle de n'importe quel type
arbitraire. Ces commandes sont gérées dans le mécanisme
de découverte pour simplement autoriser les applications à
déterminer si un ENGINE gère certaines commandes
spécifiques qu'il veut utiliser (par exemple : l'application
« foo » veut faire une requête à
différents ENGINE pour voir s'ils implémentent
« FOO_GET_VENDOR_LOGO_GIF », et ENGINE peut de ce
fait décider s'il prend en charge ou non cette extension
spécifique à « foo »).
Développements futurs¶
L'API d'ENGINE et sa structure architecturale interne sont en cours de
révision. Il est prévu d'avoir de nouvelles modifications dans
la version 0.9.8 pour une prise en charge du chargement transparent
d'ENGINE « dynamiques » (implémentés
comme bibliothèques partagées autonomes). Cela devrait autoriser
de fournir des implémentations ENGINE indépendamment des
bibliothèques d'OpenSSL ou des applications basées sur OpenSSL,
et supprimera aussi toute obligation pour les applications d'utiliser
explicitement l'ENGINE « dynamique » pour se lier
à des implémentations de bibliothèques partagées.
VOIR AUSSI¶
rsa(3),
dsa(3),
dh(3),
rand(3)
TRADUCTION¶
La traduction de cette page de manuel est maintenue par les membres de la liste
<debian-l10n-french AT lists DOT debian DOT org>. Veuillez signaler
toute erreur de traduction par un rapport de bogue sur le paquet
manpages-fr-extra.