2

openssl エンジン API には ENGINE_finish(e) と呼ばれるクリーンアップ コマンドがあり、エンジン e に実装および登録された「finish」コマンドを呼び出します。

エンジンの "finish" コマンドは、エンジンへの参照カウントが 1 の場合にのみ呼び出されます。なぜ私の場合は 3 なのかわかりません。libcurl を使用すると、呼び出し後に参照カウントも 1 ずつ増加します。メソッド POST で、4 になります。

「終了」コマンドが呼び出されることを確認するには、以下のような醜いループを実行する必要があります。他のコンポーネントに潜在的な損傷を与える可能性があるかどうかはわかりません。

    while (e->funct_ref) {
    ENGINE_finish(e);
}

エラーを示すために最初のコードを削除し、非常に単純にしました。「ダミー」エンジンのコードとクライアントのコードは次のとおりです。上記の醜いコードを使用せずに ENGINE_finish を機能させる方法と、ref カウントが 1 であるはずなのに 3 に等しい理由を教えてください。

ダミーエンジン:

#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
4

1 に答える 1

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();
于 2015-12-04T20:12:59.730 に答える