在openssl引擎API中有一个名为ENGINE_finish(e)的清理命令,它命令"完成"命令在引擎中实现和注册e。
引擎"完成"只有当引擎的引用计数等于1时才会调用该命令。我不明白为什么在我的情况下它是3.当你使用libcurl时,你的引用计数也会增加1调用方法POST,它将是4.
确保"完成"命令被调用,我需要运行如下的丑陋循环,我不知道它是否会对其他组件造成潜在的损害:
while (e->funct_ref) {
ENGINE_finish(e);
}
我已经删除了我的初始代码以演示错误并使其变得非常简单。 "虚拟"的代码引擎和客户的代码如下。请告诉我如何使ENGINE_finish在没有上面的丑陋代码的情况下工作,以及为什么ref count等于3,而它应该是1。
虚拟引擎:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <openssl/ssl.h>
#include <openssl/crypto.h>
#include <openssl/ecdsa.h>
#include <openssl/engine.h>
#include <openssl/eng_int.h>
static int dummy_destroy(ENGINE *e);
static int dummy_init(ENGINE *e);
static int dummy_finish(ENGINE *e);
static int dummy_ctrl(ENGINE *e, int cmd, long i, void *p,
void (*f) ());
static int dummy_rand_bytes(unsigned char *buf, int num);
static void dummy_rand_seed(const void *buf, int num);
static void dummy_rand_seed(const void *buf, int num) {
}
static void dummy_rand_cleanup();
static void dummy_rand_cleanup(){
}
static void dummy_rand_add(const void *buf, int num, double entropy);
static void dummy_rand_add(const void *buf, int num, double entropy){
}
static int dummy_rand_status(){
fprintf(stderr, "dummy_rand_status\n");
return 1;
}
# define DUMMY_CMD_SO_PATH ENGINE_CMD_BASE
static const ENGINE_CMD_DEFN dummy_cmd_defns[] = {
{DUMMY_CMD_SO_PATH,
"SO_PATH",
"Specifies the path to the dummy shared library",
ENGINE_CMD_FLAG_STRING},
{0, NULL, NULL, 0}
};
static char *my_prog = "dummy";
static EC_KEY *ec_key=NULL;
static char *key_file = "path-to-ec-key";
int dummy_ecdsa_sign_setup (EC_KEY *eckey, BN_CTX *ctx_in, BIGNUM **kinvp,
BIGNUM **rp);
static ECDSA_SIG *dummy_ecdsa_sign (const unsigned char *dgst, int dgst_len,
const BIGNUM *kinv, const BIGNUM *rp,
EC_KEY *in_eckey);
int dummy_ecdsa_do_verify (const unsigned char *digest, int digest_len,
const ECDSA_SIG *ecdsa_sig, EC_KEY *eckey);
static ECDSA_METHOD dummy_ecdsa = {
"dummy ECDSA method",
dummy_ecdsa_sign,
dummy_ecdsa_sign_setup,
dummy_ecdsa_do_verify,
0, /* flags */
NULL /* app_data */
};
static RAND_METHOD dummy_rand = {
/* "Cluster Labs RAND method", */
dummy_rand_seed, /* seed */
dummy_rand_bytes, /* bytes */
dummy_rand_cleanup, /* cleanup */
dummy_rand_add, /* add */
dummy_rand_bytes, /* pseudorand */
dummy_rand_status, /* status */
};
static const char *engine_dummy_id = "dummy";
static const char *engine_dummy_name =
"DUMMY Secure Element Support";
/* engine implementation */
/* ---------------------*/
static int bind_helper(ENGINE *e)
{
if (!ENGINE_set_id(e, engine_dummy_id) ||
!ENGINE_set_name(e, engine_dummy_name) ||
!ENGINE_set_RAND(e, &dummy_rand) ||
!ENGINE_set_ECDSA(e, &dummy_ecdsa) ||
!ENGINE_set_destroy_function(e, dummy_destroy) ||
!ENGINE_set_init_function(e, dummy_init) ||
!ENGINE_set_finish_function(e, dummy_finish) ||
!ENGINE_set_ctrl_function(e, dummy_ctrl) ||
!ENGINE_set_cmd_defns(e, dummy_cmd_defns))
return 0;
return 1;
}
static int dummy_destroy(ENGINE *e)
{
fprintf(stderr, "%s: DESTROYED\n", my_prog);
return 1;
}
int dummy_init(ENGINE *e)
{
fprintf(stderr, "%s: INIT ref cnt: %d\n", my_prog, e->funct_ref);
FILE *fp = fopen(key_file, "r");
if (!fp) {
fprintf(stderr,"%s: Can't open %s\n", my_prog, key_file);
return 0;
}
EVP_PKEY *pkey = PEM_read_PrivateKey(fp, NULL, 0, NULL);
if (pkey) {
ec_key = EVP_PKEY_get1_EC_KEY(pkey);
fprintf(stderr,"%s: Got ec key %p\n", my_prog, ec_key);
EVP_PKEY_free (pkey);
}
fprintf(stderr, "%s: INIT ENDS ref cnt: %d\n", my_prog, e->funct_ref);
return 1;
}
static int dummy_finish(ENGINE *e)
{
fprintf(stderr, "%s: FINISHED\n", my_prog);
return (1);
}
static int dummy_ctrl(ENGINE *e, int cmd, long i, void *p,
void (*f) ())
{
fprintf(stderr, "dummy_trl cmd %d\n", cmd);
switch (cmd) {
case DUMMY_CMD_SO_PATH:
if (p == NULL) {
return 0;
}
return 1;
default:
break;
}
return 0;
}
static int dummy_rand_bytes(unsigned char *buf, int num)
{
fprintf(stderr, "dummy_rand num = %d \n", num);
fflush(stderr);
for (int i=0; i < num; i++) {
buf[i]=rand();
}
return 1;
}
int dummy_ecdsa_sign_setup (EC_KEY *eckey, BN_CTX *ctx_in, BIGNUM **kinvp,
BIGNUM **rp)
{
return 1;
}
static ECDSA_SIG *dummy_ecdsa_sign (const unsigned char *dgst, int dgst_len,
const BIGNUM *kinv, const BIGNUM *rp,
EC_KEY *in_eckey) {
printf("dummy engine ecdsa sign digest \n");
if (ec_key != NULL) {
fprintf(stderr, "%s: got private ec_key\n", my_prog);
in_eckey = ec_key;
}
return ECDSA_do_sign_ex(dgst, dgst_len, kinv, rp, in_eckey);
}
int dummy_ecdsa_do_verify (const unsigned char *dgst, int dgst_len,
const ECDSA_SIG *ecdsa_sig, EC_KEY *eckey) {
printf("dummy engine verifying function\n");
return ECDSA_do_verify(dgst, dgst_len, ecdsa_sig, eckey);
}
# ifdef ENGINE_DYNAMIC_SUPPORT
static int bind_fn(ENGINE *e, const char *id)
{
fprintf(stderr, "bind_fn DUMMY\n");
if (id && (strcmp(id, engine_dummy_id) != 0)) {
fprintf(stderr, "bind_fn return(0) first\n");
return 0;
}
if (!bind_helper(e)) {
fprintf(stderr, "bind_fn return(1) first\n");
return 0;
}
fprintf(stderr, "bind_fn return(1) %d\n", e->funct_ref);
return 1;
}
IMPLEMENT_DYNAMIC_CHECK_FN()
IMPLEMENT_DYNAMIC_BIND_FN(bind_fn)
# endif /* ENGINE_DYNAMIC_SUPPORT */
客户代码:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <openssl/engine.h>
#include <openssl/eng_int.h>
static char *myprog = "engtest";
ENGINE *set_engine (const char * name) {
ENGINE *e = NULL;
ENGINE_load_builtin_engines();
e = ENGINE_by_id(name);
if(!e || !ENGINE_init(e)) {
#ifdef DEBUG
fprintf(stderr,"%s: can't find or init engine %s %p\n", myprog, name, e);
#endif
if (e)
ENGINE_free(e);
return NULL;
}
if (!ENGINE_set_default(e, ENGINE_METHOD_ALL)){
#ifdef DEBUG
fprintf(stderr,"%s: can't set engine %s as default %p\n", myprog, name, e);
#endif
ENGINE_finish(e);
ENGINE_free(e);
return NULL;
}
fprintf(stderr, "%s: set eng %s %p %p, ref cnt = %d \n", myprog, name, e, e->finish, e->funct_ref);
return e;
}
int unset_engine (ENGINE * e) {
if (!e)
return 0;
ENGINE_set_RAND(e, NULL);
ENGINE_set_ECDSA(e, NULL);
ENGINE_set_default(NULL, 0);
fprintf(stderr, "%s: unset %p, ref cnt = %d\n", myprog, e, e->funct_ref);
while (e->funct_ref) {
ENGINE_finish(e);
}
ENGINE_free(e);
return 1;
}
int main (int argc, char *argv[]) {
ENGINE *e = set_engine("dummy");
unset_engine(e);
return 0;
}
输出如下所示。它在初始化时为0,然后以某种方式碰到3。
bind_fn DUMMY
bind_fn return(1) 0
dummy: INIT ref cnt: 0
dummy: Got ec key 0x7f8199c04b70
dummy: INIT ENDS ref cnt: 0
engtest: set eng dummy 0x7f8199c04180 0x101bd69a0, ref cnt = 3
engtest: unset 0x7f8199c04180, ref cnt =3
dummy: FINISHED
答案 0 :(得分:2)
在OpenSSL中清理引擎(以及某些其他类型的数据)的整个机制对我来说似乎相当脆弱,你的问题看起来很熟悉。除了没有释放的内存之外,如果没有以正确的顺序执行某些清理调用,很容易遇到崩溃。我没有努力重现您的特定问题,但这里有一段代码,我用它来干净地释放我的引擎,所以你可以尝试一下:
OBJ_cleanup();
EVP_cleanup();
if (NULL != S_engine) {
ENGINE_unregister_ciphers(S_engine);
ENGINE_unregister_digests(S_engine);
ENGINE_unregister_ECDH(S_engine);
ENGINE_unregister_ECDSA(S_engine);
ENGINE_unregister_RAND(S_engine);
ENGINE_finish(S_engine);
ENGINE_remove(S_engine);
S_engine = NULL;
}
ENGINE_cleanup();