Commit 839590f5 authored by Richard Levitte's avatar Richard Levitte
Browse files

- Add the possibility to control engines through control names but

  with arbitrary arguments instead of just a string.
- Change the key loaders to take a UI_METHOD instead of a callback
  function pointer.  NOTE: this breaks binary compatibility with
  earlier versions of OpenSSL [engine].
- Addapt the nCipher code for these new conditions and add a card
  insertion callback.
parent e0a8d1f9
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -11,6 +11,15 @@
         *) applies to 0.9.6a (/0.9.6b) and 0.9.7
         +) applies to 0.9.7 only

  +) Add the possibility to control engines through control names but with
     arbitrary arguments instead of just a string.
     Change the key loaders to take a UI_METHOD instead of a callback
     function pointer.  NOTE: this breaks binary compatibility with earlier
     versions of OpenSSL [engine].
     Addapt the nCipher code for these new conditions and add a card insertion
     callback.
     [Richard Levitte]

  +) Enhance the general user interface with mechanisms to better support
     dialog box interfaces, application-defined prompts, the possibility
     to use defaults (for example default passwords from somewhere else)
+22 −3
Original line number Diff line number Diff line
@@ -72,6 +72,7 @@
#include <openssl/rand.h>
#include <openssl/evp.h>
#include <openssl/pem.h>
#include <openssl/ui.h>
#include <openssl/symhacks.h>

#ifdef  __cplusplus
@@ -136,6 +137,10 @@ typedef void DH_METHOD;
/* Indicates that the control command takes *no* input. Ie. the control command
 * is unparameterised. */
#define ENGINE_CMD_FLAG_NO_INPUT	(unsigned int)0x0004
/* Indicates that the control command is internal. This control command won't
 * be shown in any output, and is only usable through the ENGINE_ctrl_cmd()
 * function. */
#define ENGINE_CMD_FLAG_INTERNAL	(unsigned int)0x0008

/* NB: These 3 control commands are deprecated and should not be used. ENGINEs
 * relying on these commands should compile conditional support for
@@ -154,6 +159,11 @@ typedef void DH_METHOD;
#define ENGINE_CTRL_SET_PASSWORD_CALLBACK	2
#define ENGINE_CTRL_HUP				3 /* Close and reinitialise any
						     handles/connections etc. */
#define ENGINE_CTRL_SET_USER_INTERFACE          4 /* Alternative to callback */
#define ENGINE_CTRL_SET_CALLBACK_DATA           5 /* User-specific data, used
                                                     when calling the password
                                                     callback and the user
                                                     interface */

/* These control commands allow an application to deal with an arbitrary engine
 * in a dynamic way. Warn: Negative return values indicate errors FOR THESE
@@ -264,7 +274,7 @@ typedef int (*ENGINE_GEN_INT_FUNC_PTR)(ENGINE *);
typedef int (*ENGINE_CTRL_FUNC_PTR)(ENGINE *, int, long, void *, void (*f)());
/* Generic load_key function pointer */
typedef EVP_PKEY * (*ENGINE_LOAD_KEY_PTR)(ENGINE *, const char *,
	pem_password_cb *callback, void *callback_data);
	UI_METHOD *ui_method, void *callback_data);

/* STRUCTURE functions ... all of these functions deal with pointers to ENGINE
 * structures where the pointers have a "structural reference". This means that
@@ -312,6 +322,13 @@ int ENGINE_ctrl(ENGINE *e, int cmd, long i, void *p, void (*f)());
 * ENGINE_ctrl_cmd_string(), only ENGINE_ctrl(). */
int ENGINE_cmd_is_executable(ENGINE *e, int cmd);

/* This function works like ENGINE_ctrl() with the exception of taking a
 * command name instead of a command number, and can handle optional commands.
 * See the comment on ENGINE_ctrl_cmd_string() for an explanation on how to
 * use the cmd_name and cmd_optional. */
int ENGINE_ctrl_cmd(ENGINE *e, const char *cmd_name,
        long i, void *p, void (*f)(), int cmd_optional);

/* This function passes a command-name and argument to an ENGINE. The cmd_name
 * is converted to a command number and the control command is called using
 * 'arg' as an argument (unless the ENGINE doesn't support such a command, in
@@ -419,9 +436,9 @@ int ENGINE_finish(ENGINE *e);
 * location, handled by the engine.  The storage may be on a card or
 * whatever. */
EVP_PKEY *ENGINE_load_private_key(ENGINE *e, const char *key_id,
	pem_password_cb *callback, void *callback_data);
	UI_METHOD *ui_method, void *callback_data);
EVP_PKEY *ENGINE_load_public_key(ENGINE *e, const char *key_id,
	pem_password_cb *callback, void *callback_data);
	UI_METHOD *ui_method, void *callback_data);

/* This returns a pointer for the current ENGINE structure that
 * is (by default) performing any RSA operations. The value returned
@@ -486,6 +503,7 @@ void ERR_load_ENGINE_strings(void);
#define ENGINE_F_ENGINE_BY_ID				 106
#define ENGINE_F_ENGINE_CMD_IS_EXECUTABLE		 170
#define ENGINE_F_ENGINE_CTRL				 142
#define ENGINE_F_ENGINE_CTRL_CMD			 178
#define ENGINE_F_ENGINE_CTRL_CMD_STRING			 171
#define ENGINE_F_ENGINE_FINISH				 107
#define ENGINE_F_ENGINE_FREE				 108
@@ -507,6 +525,7 @@ void ERR_load_ENGINE_strings(void);
#define ENGINE_F_HWCRHK_FINISH				 135
#define ENGINE_F_HWCRHK_GET_PASS			 155
#define ENGINE_F_HWCRHK_INIT				 136
#define ENGINE_F_HWCRHK_INSERT_CARD			 179
#define ENGINE_F_HWCRHK_LOAD_PRIVKEY			 153
#define ENGINE_F_HWCRHK_LOAD_PUBKEY			 154
#define ENGINE_F_HWCRHK_MOD_EXP				 137
+2 −0
Original line number Diff line number Diff line
@@ -83,6 +83,7 @@ static ERR_STRING_DATA ENGINE_str_functs[]=
{ERR_PACK(0,ENGINE_F_ENGINE_BY_ID,0),	"ENGINE_by_id"},
{ERR_PACK(0,ENGINE_F_ENGINE_CMD_IS_EXECUTABLE,0),	"ENGINE_cmd_is_executable"},
{ERR_PACK(0,ENGINE_F_ENGINE_CTRL,0),	"ENGINE_ctrl"},
{ERR_PACK(0,ENGINE_F_ENGINE_CTRL_CMD,0),	"ENGINE_ctrl_cmd"},
{ERR_PACK(0,ENGINE_F_ENGINE_CTRL_CMD_STRING,0),	"ENGINE_ctrl_cmd_string"},
{ERR_PACK(0,ENGINE_F_ENGINE_FINISH,0),	"ENGINE_finish"},
{ERR_PACK(0,ENGINE_F_ENGINE_FREE,0),	"ENGINE_free"},
@@ -104,6 +105,7 @@ static ERR_STRING_DATA ENGINE_str_functs[]=
{ERR_PACK(0,ENGINE_F_HWCRHK_FINISH,0),	"HWCRHK_FINISH"},
{ERR_PACK(0,ENGINE_F_HWCRHK_GET_PASS,0),	"HWCRHK_GET_PASS"},
{ERR_PACK(0,ENGINE_F_HWCRHK_INIT,0),	"HWCRHK_INIT"},
{ERR_PACK(0,ENGINE_F_HWCRHK_INSERT_CARD,0),	"HWCRHK_INSERT_CARD"},
{ERR_PACK(0,ENGINE_F_HWCRHK_LOAD_PRIVKEY,0),	"HWCRHK_LOAD_PRIVKEY"},
{ERR_PACK(0,ENGINE_F_HWCRHK_LOAD_PUBKEY,0),	"HWCRHK_LOAD_PUBKEY"},
{ERR_PACK(0,ENGINE_F_HWCRHK_MOD_EXP,0),	"HWCRHK_MOD_EXP"},
+41 −4
Original line number Diff line number Diff line
@@ -232,7 +232,7 @@ int ENGINE_finish(ENGINE *e)
	}

EVP_PKEY *ENGINE_load_private_key(ENGINE *e, const char *key_id,
	pem_password_cb *callback, void *callback_data)
	UI_METHOD *ui_method, void *callback_data)
	{
	EVP_PKEY *pkey;

@@ -257,7 +257,7 @@ EVP_PKEY *ENGINE_load_private_key(ENGINE *e, const char *key_id,
			ENGINE_R_NO_LOAD_FUNCTION);
		return 0;
		}
	pkey = e->load_privkey(e, key_id, callback, callback_data);
	pkey = e->load_privkey(e, key_id, ui_method, callback_data);
	if (!pkey)
		{
		ENGINEerr(ENGINE_F_ENGINE_LOAD_PRIVATE_KEY,
@@ -268,7 +268,7 @@ EVP_PKEY *ENGINE_load_private_key(ENGINE *e, const char *key_id,
	}

EVP_PKEY *ENGINE_load_public_key(ENGINE *e, const char *key_id,
	pem_password_cb *callback, void *callback_data)
	UI_METHOD *ui_method, void *callback_data)
	{
	EVP_PKEY *pkey;

@@ -293,7 +293,7 @@ EVP_PKEY *ENGINE_load_public_key(ENGINE *e, const char *key_id,
			ENGINE_R_NO_LOAD_FUNCTION);
		return 0;
		}
	pkey = e->load_pubkey(e, key_id, callback, callback_data);
	pkey = e->load_pubkey(e, key_id, ui_method, callback_data);
	if (!pkey)
		{
		ENGINEerr(ENGINE_F_ENGINE_LOAD_PUBLIC_KEY,
@@ -487,6 +487,43 @@ int ENGINE_cmd_is_executable(ENGINE *e, int cmd)
	return 1;
	}

int ENGINE_ctrl_cmd(ENGINE *e, const char *cmd_name,
        long i, void *p, void (*f)(), int cmd_optional)
        {
	int num;

	if((e == NULL) || (cmd_name == NULL))
		{
		ENGINEerr(ENGINE_F_ENGINE_CTRL_CMD_STRING,
			ERR_R_PASSED_NULL_PARAMETER);
		return 0;
		}
	if((e->ctrl == NULL) || ((num = ENGINE_ctrl(e,
					ENGINE_CTRL_GET_CMD_FROM_NAME,
					0, (void *)cmd_name, NULL)) <= 0))
		{
		/* If the command didn't *have* to be supported, we fake
		 * success. This allows certain settings to be specified for
		 * multiple ENGINEs and only require a change of ENGINE id
		 * (without having to selectively apply settings). Eg. changing
		 * from a hardware device back to the regular software ENGINE
		 * without editing the config file, etc. */
		if(cmd_optional)
			{
			ERR_clear_error();
			return 1;
			}
		ENGINEerr(ENGINE_F_ENGINE_CTRL_CMD,
			ENGINE_R_INVALID_CMD_NAME);
		return 0;
		}
	/* Force the result of the control command to 0 or 1, for the reasons
	 * mentioned before. */
        if (ENGINE_ctrl(e, num, i, p, f))
                return 1;
        return 0;
        }

int ENGINE_ctrl_cmd_string(ENGINE *e, const char *cmd_name, const char *arg,
				int cmd_optional)
	{
+161 −37
Original line number Diff line number Diff line
@@ -64,6 +64,7 @@
#include "cryptlib.h"
#include <openssl/dso.h>
#include <openssl/engine.h>
#include <openssl/ui.h>

#ifndef OPENSSL_NO_HW
#ifndef OPENSSL_NO_HW_NCIPHER
@@ -116,13 +117,17 @@ static int hwcrhk_rand_status(void);

/* KM stuff */
static EVP_PKEY *hwcrhk_load_privkey(ENGINE *eng, const char *key_id,
	pem_password_cb *callback, void *callback_data);
	UI_METHOD *ui_method, void *callback_data);
static EVP_PKEY *hwcrhk_load_pubkey(ENGINE *eng, const char *key_id,
	pem_password_cb *callback, void *callback_data);
	UI_METHOD *ui_method, void *callback_data);
static void hwcrhk_ex_free(void *obj, void *item, CRYPTO_EX_DATA *ad,
	int ind,long argl, void *argp);

/* Interaction stuff */
static int hwcrhk_insert_card(const char *prompt_info,
	const char *wrong_info,
	HWCryptoHook_PassphraseContext *ppctx,
	HWCryptoHook_CallerContext *cactx);
static int hwcrhk_get_pass(const char *prompt_info,
	int *len_io, char *buf,
	HWCryptoHook_PassphraseContext *ppctx,
@@ -133,6 +138,8 @@ static void hwcrhk_log_message(void *logstr, const char *message);
#define HWCRHK_CMD_SO_PATH		ENGINE_CMD_BASE
#define HWCRHK_CMD_FORK_CHECK		(ENGINE_CMD_BASE + 1)
#define HWCRHK_CMD_THREAD_LOCKING	(ENGINE_CMD_BASE + 2)
#define HWCRHK_CMD_SET_USER_INTERFACE   (ENGINE_CMD_BASE + 3)
#define HWCRHK_CMD_SET_CALLBACK_DATA    (ENGINE_CMD_BASE + 4)
static const ENGINE_CMD_DEFN hwcrhk_cmd_defns[] = {
	{HWCRHK_CMD_SO_PATH,
		"SO_PATH",
@@ -146,6 +153,14 @@ static const ENGINE_CMD_DEFN hwcrhk_cmd_defns[] = {
		"THREAD_LOCKING",
		"Turns thread-safe locking on or off (boolean)",
		ENGINE_CMD_FLAG_NUMERIC},
	{HWCRHK_CMD_SET_USER_INTERFACE,
		"SET_USER_INTERFACE",
		"Set the global user interface (internal)",
		ENGINE_CMD_FLAG_INTERNAL},
	{HWCRHK_CMD_SET_CALLBACK_DATA,
		"SET_CALLBACK_DATA",
		"Set the global user interface extra data (internal)",
		ENGINE_CMD_FLAG_INTERNAL},
	{0, NULL, NULL, 0}
	};

@@ -214,7 +229,7 @@ struct HWCryptoHook_MutexValue
   into HWCryptoHook_PassphraseContext */
struct HWCryptoHook_PassphraseContextValue
	{
	pem_password_cb *password_callback; /* If != NULL, will be called */
        UI_METHOD *ui_method;
	void *callback_data;
	};

@@ -223,7 +238,10 @@ struct HWCryptoHook_PassphraseContextValue
   into HWCryptoHook_CallerContext */
struct HWCryptoHook_CallerContextValue
	{
	void *any;
	pem_password_cb *password_callback; /* Deprecated!  Only present for
                                               backward compatibility! */
        UI_METHOD *ui_method;
	void *callback_data;
	};

/* The MPI structure in HWCryptoHook is pretty compatible with OpenSSL
@@ -233,28 +251,24 @@ struct HWCryptoHook_CallerContextValue
#define MPI2BN(bn, mp) \
    {mp.size = bn->dmax * sizeof(BN_ULONG); mp.buf = (unsigned char *)bn->d;}

#if 0 /* Card and password management is not yet supported */
/* HWCryptoHook callbacks.  insert_card() and get_pass() are not yet
   defined, because we haven't quite decided on the proper form yet.
   log_message() just adds an entry in the error stack.  I don't know
   if that's good or bad...  */
static int insert_card(const char *prompt_info,
	const char *wrong_info,
	HWCryptoHook_PassphraseContext *ppctx,
	HWCryptoHook_CallerContext *cactx);
static int get_pass(const char *prompt_info,
	int *len_io, char *buf,
	HWCryptoHook_PassphraseContext *ppctx,
	HWCryptoHook_CallerContext *cactx);
#endif

static BIO *logstream = NULL;
static pem_password_cb *password_callback = NULL;
#if 0
static void *password_callback_userdata = NULL;
#endif
static int disable_mutex_callbacks = 0;

/* One might wonder why these are needed, since one can pass down at least
   a UI_METHOD and a pointer to callback data to the key-loading functions.
   The thing is that the ModExp and RSAImmed functions can load keys as well,
   if the data they get is in a special, nCipher-defined format (hint: if you
   look at the private exponent of the RSA data as a string, you'll see this
   string: "nCipher KM tool key id", followed by some bytes, followed a key
   identity string, followed by more bytes.  This happens when you use "embed"
   keys instead of "hwcrhk" keys).  Unfortunately, those functions do not take
   any passphrase or caller context, and our functions can't really take any
   callback data either.  Still, the "insert_card" and "get_passphrase"
   callbacks may be called down the line, and will need to know what user
   interface callbacks to call, and having callback data from the application
   may be a nice thing as well, so we need to keep track of that globally. */
static HWCryptoHook_CallerContext password_context = { NULL, NULL, NULL };

/* Stuff to pass to the HWCryptoHook library */
static HWCryptoHook_InitInfo hwcrhk_globals = {
	0,			/* Flags */
@@ -291,7 +305,7 @@ static HWCryptoHook_InitInfo hwcrhk_globals = {
	0, /* hwcrhk_cv_destroy, */

	hwcrhk_get_pass,	/* pass phrase */
	0, /* insert_card, */	/* insert a card */
	hwcrhk_insert_card,	/* insert a card */
	hwcrhk_log_message	/* Log message */
};

@@ -406,7 +420,8 @@ static const char *n_hwcrhk_ModExpCRT = "HWCryptoHook_ModExpCRT";
 * called, the checking and error handling is probably down there. */

/* utility function to obtain a context */
static int get_context(HWCryptoHook_ContextHandle *hac)
static int get_context(HWCryptoHook_ContextHandle *hac,
        HWCryptoHook_CallerContext *cac)
	{
	char tempbuf[1024];
	HWCryptoHook_ErrMsgBuf rmsg;
@@ -415,7 +430,7 @@ static int get_context(HWCryptoHook_ContextHandle *hac)
	rmsg.size = 1024;

        *hac = p_hwcrhk_Init(&hwcrhk_globals, sizeof(hwcrhk_globals), &rmsg,
		NULL);
		cac);
	if (!*hac)
                return 0;
        return 1;
@@ -506,7 +521,7 @@ static int hwcrhk_init(ENGINE *e)

	/* Try and get a context - if not, we may have a DSO but no
	 * accelerator! */
	if(!get_context(&hwcrhk_context))
	if(!get_context(&hwcrhk_context, &password_context))
		{
		ENGINEerr(ENGINE_F_HWCRHK_INIT,ENGINE_R_UNIT_FAILURE);
		goto err;
@@ -609,7 +624,17 @@ static int hwcrhk_ctrl(ENGINE *e, int cmd, long i, void *p, void (*f)())
		break;
	case ENGINE_CTRL_SET_PASSWORD_CALLBACK:
		CRYPTO_w_lock(CRYPTO_LOCK_ENGINE);
		password_callback = (pem_password_cb *)f;
		password_context.password_callback = (pem_password_cb *)f;
		CRYPTO_w_unlock(CRYPTO_LOCK_ENGINE);
		break;
	case ENGINE_CTRL_SET_USER_INTERFACE:
		CRYPTO_w_lock(CRYPTO_LOCK_ENGINE);
		password_context.ui_method = (UI_METHOD *)p;
		CRYPTO_w_unlock(CRYPTO_LOCK_ENGINE);
		break;
	case ENGINE_CTRL_SET_CALLBACK_DATA:
		CRYPTO_w_lock(CRYPTO_LOCK_ENGINE);
		password_context.callback_data = p;
		CRYPTO_w_unlock(CRYPTO_LOCK_ENGINE);
		break;
	/* this enables or disables the "SimpleForkCheck" flag used in the
@@ -653,7 +678,7 @@ static int hwcrhk_ctrl(ENGINE *e, int cmd, long i, void *p, void (*f)())
	}

static EVP_PKEY *hwcrhk_load_privkey(ENGINE *eng, const char *key_id,
	pem_password_cb *callback, void *callback_data)
	UI_METHOD *ui_method, void *callback_data)
	{
#ifndef OPENSSL_NO_RSA
	RSA *rtmp = NULL;
@@ -682,7 +707,7 @@ static EVP_PKEY *hwcrhk_load_privkey(ENGINE *eng, const char *key_id,
			ERR_R_MALLOC_FAILURE);
		goto err;
		}
	ppctx.password_callback = callback;
        ppctx.ui_method = ui_method;
	ppctx.callback_data = callback_data;
	if (p_hwcrhk_RSALoadKey(hwcrhk_context, key_id, hptr,
		&rmsg, &ppctx))
@@ -752,12 +777,13 @@ static EVP_PKEY *hwcrhk_load_privkey(ENGINE *eng, const char *key_id,
	}

static EVP_PKEY *hwcrhk_load_pubkey(ENGINE *eng, const char *key_id,
	pem_password_cb *callback, void *callback_data)
	UI_METHOD *ui_method, void *callback_data)
	{
	EVP_PKEY *res = NULL;

#ifndef OPENSSL_NO_RSA
        res = hwcrhk_load_privkey(eng, key_id, callback, callback_data);
        res = hwcrhk_load_privkey(eng, key_id,
                ui_method, callback_data);
#endif

	if (res)
@@ -1084,28 +1110,126 @@ static int hwcrhk_get_pass(const char *prompt_info,
	HWCryptoHook_PassphraseContext *ppctx,
	HWCryptoHook_CallerContext *cactx)
	{
	pem_password_cb *callback = password_callback;
	pem_password_cb *callback = NULL;
	void *callback_data = NULL;
        UI_METHOD *ui_method = NULL;

        if (cactx)
                {
                if (cactx->ui_method)
                        ui_method = cactx->ui_method;
		if (cactx->password_callback)
			callback = cactx->password_callback;
		if (cactx->callback_data)
			callback_data = cactx->callback_data;
                }
	if (ppctx)
		{
		if (ppctx->password_callback)
			callback = ppctx->password_callback;
                if (ppctx->ui_method)
                        {
                        ui_method = ppctx->ui_method;
                        callback = NULL;
                        }
		if (ppctx->callback_data)
			callback_data = ppctx->callback_data;
		}
	if (callback == NULL)
	if (callback == NULL && ui_method == NULL)
		{
		ENGINEerr(ENGINE_F_HWCRHK_GET_PASS,ENGINE_R_NO_CALLBACK);
		return -1;
		}

        if (ui_method)
                {
                UI *ui = UI_new_method(ui_method);
                if (ui)
                        {
                        int ok;
                        char *prompt = UI_construct_prompt(ui,
                                "pass phrase", prompt_info);

                        ok = UI_add_input_string(ui,prompt,
                                UI_INPUT_FLAG_DEFAULT_PWD,
				buf,0,(*len_io) - 1);
                        UI_add_user_data(ui, callback_data);
                        if (ok >= 0)
                                ok=UI_process(ui);
                        if (ok >= 0)
                                *len_io = strlen(buf);

                        OPENSSL_free(prompt);
                        UI_free(ui);
                        }
                }
        else
                {
                *len_io = callback(buf, *len_io, 0, callback_data);
                }
	if(!*len_io)
		return -1;
	return 0;
	}

static int hwcrhk_insert_card(const char *prompt_info,
		      const char *wrong_info,
		      HWCryptoHook_PassphraseContext *ppctx,
		      HWCryptoHook_CallerContext *cactx)
        {
        int ok = 1;
        UI *ui;
	void *callback_data = NULL;
        UI_METHOD *ui_method = NULL;

        if (cactx)
                {
                if (cactx->ui_method)
                        ui_method = cactx->ui_method;
		if (cactx->callback_data)
			callback_data = cactx->callback_data;
                }
	if (ppctx)
		{
                if (ppctx->ui_method)
                        ui_method = ppctx->ui_method;
		if (ppctx->callback_data)
			callback_data = ppctx->callback_data;
		}
	if (ui_method == NULL)
		{
		ENGINEerr(ENGINE_F_HWCRHK_INSERT_CARD,ENGINE_R_NO_CALLBACK);
		return -1;
		}

        ui = UI_new_method(ui_method);

        if (ui)
                {
                char answer[10];
                char buf[BUFSIZ];

                if (wrong_info)
                        BIO_snprintf(buf, sizeof(buf)-1,
                                "Current card: \"%s\"\n", wrong_info);
                ok = UI_dup_info_string(ui, buf);
                if (ok == 0 && prompt_info)
                        {
                        BIO_snprintf(buf, sizeof(buf)-1,
                                "Insert card \"%s\"\n then hit <enter> or C<enter> to cancel\n", prompt_info);
                        ok = UI_dup_input_string(ui, buf, 1,
                                answer, 0, sizeof(answer)-1);
                        }
                UI_add_user_data(ui, callback_data);
                if (ok == 0)
                        ok = UI_process(ui);
                UI_free(ui);
                if (strchr("Cc",answer[0]) == 0)
                        ok = 1;
                }
        if (ok == 0)
                return 0;
        return -1;
        }

static void hwcrhk_log_message(void *logstr, const char *message)
	{
	BIO *lstream = NULL;