mirror of
https://git.gnunet.org/libmicrohttpd.git
synced 2026-09-25 04:09:31 +03:00
780 lines
16 KiB
C
780 lines
16 KiB
C
/* main.c
|
|
* Copyright (C) 2001, 2002, 2003, 2007 Timo Schulz
|
|
*
|
|
* This file is part of OpenCDK.
|
|
*
|
|
* OpenCDK 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 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* OpenCDK 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.
|
|
*/
|
|
#ifdef HAVE_CONFIG_H
|
|
# include <config.h>
|
|
#endif
|
|
#include <stdio.h>
|
|
#include <errno.h>
|
|
#ifdef HAVE_UNISTD_H
|
|
# include <unistd.h>
|
|
#endif
|
|
#ifdef _WIN32
|
|
#include <windows.h>
|
|
#endif
|
|
|
|
#include "opencdk.h"
|
|
#include "main.h"
|
|
#include "packet.h"
|
|
|
|
|
|
/* Set a default cipher algorithm and a digest algorithm.
|
|
Even if AES and SHA-256 are not 'MUST' in the latest
|
|
OpenPGP draft, AES seems to be a good choice. */
|
|
#define DEFAULT_CIPHER_ALGO GCRY_CIPHER_AES
|
|
#define DEFAULT_DIGEST_ALGO GCRY_MD_SHA256
|
|
|
|
/* The site of the secure memory which is allocated in gcrypt. */
|
|
#define SECMEM_SIZE 16384
|
|
|
|
|
|
/* Hooks to custom memory allocation functions. */
|
|
static void *(*alloc_func) (size_t n) = gcry_xmalloc;
|
|
static void *(*alloc_secure_func) (size_t n) = gcry_malloc_secure;
|
|
static void *(*realloc_func) (void *p, size_t n) = gcry_realloc;
|
|
static void *(*calloc_func) (size_t m, size_t n) = gcry_calloc;
|
|
static void (*free_func) (void *) = gcry_free;
|
|
static int malloc_hooks = 0;
|
|
static int secmem_init = 0;
|
|
|
|
/* Global settings for the logging. */
|
|
static cdk_log_fnc_t log_handler = NULL;
|
|
static void *log_handler_value = NULL;
|
|
static int log_level = CDK_LOG_NONE;
|
|
|
|
|
|
/**
|
|
* cdk_strerror:
|
|
* @ec: the error number
|
|
*
|
|
* Return an error text for the given id.
|
|
**/
|
|
const char *
|
|
cdk_strerror (int ec)
|
|
{
|
|
static char buf[20];
|
|
|
|
switch (ec)
|
|
{
|
|
case CDK_EOF:
|
|
return "End Of File";
|
|
case CDK_Success:
|
|
return "No error";
|
|
case CDK_General_Error:
|
|
return "General error";
|
|
case CDK_File_Error:
|
|
return strerror (errno);
|
|
case CDK_Bad_Sig:
|
|
return "Bad signature";
|
|
case CDK_Inv_Packet:
|
|
return "Invalid packet";
|
|
case CDK_Inv_Algo:
|
|
return "Invalid algorithm";
|
|
case CDK_Not_Implemented:
|
|
return "This is not implemented yet";
|
|
case CDK_Armor_Error:
|
|
return "ASCII armor error";
|
|
case CDK_Armor_CRC_Error:
|
|
return "ASCII armored damaged (CRC error)";
|
|
case CDK_MPI_Error:
|
|
return "Invalid or missformed MPI";
|
|
case CDK_Inv_Value:
|
|
return "Invalid parameter or value";
|
|
case CDK_Error_No_Key:
|
|
return "No key available or not found";
|
|
case CDK_Chksum_Error:
|
|
return "Check for key does not match";
|
|
case CDK_Time_Conflict:
|
|
return "Time conflict";
|
|
case CDK_Zlib_Error:
|
|
return "ZLIB error";
|
|
case CDK_Weak_Key:
|
|
return "Weak key was detected";
|
|
case CDK_Out_Of_Core:
|
|
return "Out of core!!";
|
|
case CDK_Wrong_Seckey:
|
|
return "Wrong secret key";
|
|
case CDK_Wrong_Format:
|
|
return "Data has wrong format";
|
|
case CDK_Bad_MDC:
|
|
return "Manipulated MDC detected";
|
|
case CDK_Inv_Mode:
|
|
return "Invalid mode";
|
|
case CDK_Error_No_Keyring:
|
|
return "No keyring available";
|
|
case CDK_Inv_Packet_Ver:
|
|
return "Invalid version for packet";
|
|
case CDK_Too_Short:
|
|
return "Buffer or object is too short";
|
|
case CDK_Unusable_Key:
|
|
return "Unusable public key";
|
|
case CDK_No_Data:
|
|
return "No data";
|
|
case CDK_No_Passphrase:
|
|
return "No passphrase supplied";
|
|
case CDK_Network_Error:
|
|
return "A network error occurred";
|
|
default:
|
|
sprintf (buf, "ec=%d", ec);
|
|
return buf;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
static void
|
|
out_of_core (size_t n)
|
|
{
|
|
fprintf (stderr, "\n ** fatal error: out of memory (%d bytes) **\n", n);
|
|
}
|
|
|
|
|
|
/**
|
|
* cdk_set_malloc_hooks:
|
|
* @new_alloc_func: malloc replacement
|
|
* @new_alloc_secure_func: secure malloc replacement
|
|
* @new_realloc_func: realloc replacement
|
|
* @new_calloc_func: calloc replacement
|
|
* @new_free_func: free replacement
|
|
*
|
|
* Set private memory hooks for the library.
|
|
*/
|
|
void
|
|
cdk_set_malloc_hooks (void *(*new_alloc_func) (size_t n),
|
|
void *(*new_alloc_secure_func) (size_t n),
|
|
void *(*new_realloc_func) (void *p, size_t n),
|
|
void *(*new_calloc_func) (size_t m, size_t n),
|
|
void (*new_free_func) (void *))
|
|
{
|
|
alloc_func = new_alloc_func;
|
|
alloc_secure_func = new_alloc_secure_func;
|
|
realloc_func = new_realloc_func;
|
|
calloc_func = new_calloc_func;
|
|
free_func = new_free_func;
|
|
malloc_hooks = 1;
|
|
}
|
|
|
|
|
|
/**
|
|
* cdk_malloc_hook_initialized:
|
|
*
|
|
* Return if the malloc hooks are already initialized.
|
|
**/
|
|
int
|
|
cdk_malloc_hook_initialized (void)
|
|
{
|
|
return malloc_hooks;
|
|
}
|
|
|
|
|
|
void *
|
|
cdk_malloc (size_t size)
|
|
{
|
|
void *p = alloc_func (size);
|
|
if (!p)
|
|
out_of_core (size);
|
|
return p;
|
|
}
|
|
|
|
|
|
/**
|
|
* cdk_calloc:
|
|
* @n: amount of elements
|
|
* @m: size of one element
|
|
*
|
|
* Safe wrapper around the c-function calloc.
|
|
**/
|
|
void *
|
|
cdk_calloc (size_t n, size_t m)
|
|
{
|
|
void *p = calloc_func (n, m);
|
|
if (!p)
|
|
out_of_core (m);
|
|
return p;
|
|
}
|
|
|
|
|
|
/* Things which need to be done after the secure memory initialisation. */
|
|
static void
|
|
_secmem_finish (void)
|
|
{
|
|
gcry_control (GCRYCTL_DROP_PRIVS);
|
|
}
|
|
|
|
|
|
/* Initialize the secure memory. */
|
|
static void
|
|
_secmem_init (size_t size)
|
|
{
|
|
if (secmem_init == 1)
|
|
return;
|
|
if (size >= SECMEM_SIZE)
|
|
size = SECMEM_SIZE;
|
|
|
|
/* Check if no other library has already initialized gcrypt. */
|
|
if (!gcry_control (GCRYCTL_ANY_INITIALIZATION_P))
|
|
{
|
|
_cdk_log_debug ("init: libgcrypt initialize.\n");
|
|
gcry_control (GCRYCTL_INIT_SECMEM, size, 0);
|
|
gcry_control (GCRYCTL_USE_SECURE_RNDPOOL);
|
|
gcry_control (GCRYCTL_DISABLE_SECMEM_WARN);
|
|
gcry_control (GCRYCTL_INITIALIZATION_FINISHED, NULL, 0);
|
|
secmem_init = 1;
|
|
}
|
|
}
|
|
|
|
|
|
/* Things which needs to be done to deinit the secure memory. */
|
|
static void
|
|
_secmem_end (void)
|
|
{
|
|
gcry_control (GCRYCTL_TERM_SECMEM);
|
|
secmem_init = 0;
|
|
}
|
|
|
|
|
|
/* The Windows system needs to startup the Winsock interface first
|
|
before we can use any socket related function. */
|
|
#ifdef _WIN32
|
|
static void
|
|
init_sockets (void)
|
|
{
|
|
static int initialized = 0;
|
|
WSADATA wsdata;
|
|
|
|
if (initialized)
|
|
return;
|
|
if (WSAStartup (0x202, &wsdata))
|
|
_cdk_log_debug ("winsock init failed.\n");
|
|
|
|
initialized = 1;
|
|
}
|
|
|
|
static void
|
|
deinit_sockets (void)
|
|
{
|
|
WSACleanup ();
|
|
}
|
|
#else
|
|
void
|
|
init_sockets (void)
|
|
{
|
|
}
|
|
void
|
|
deinit_sockets (void)
|
|
{
|
|
}
|
|
#endif
|
|
|
|
|
|
/**
|
|
* cdk_lib_startup:
|
|
*
|
|
* Prepare the internal structures of the library.
|
|
* This function should be called before any other CDK function.
|
|
*/
|
|
void
|
|
cdk_lib_startup (void)
|
|
{
|
|
_secmem_init (SECMEM_SIZE);
|
|
_secmem_finish ();
|
|
init_sockets ();
|
|
}
|
|
|
|
|
|
/**
|
|
* cdk_lib_shutdown:
|
|
*
|
|
* Shutdown the library and free all internal and globally used
|
|
* memory and structures. This function should be called in the
|
|
* exit handler of the calling program.
|
|
*/
|
|
void
|
|
cdk_lib_shutdown (void)
|
|
{
|
|
deinit_sockets ();
|
|
_secmem_end ();
|
|
}
|
|
|
|
/**
|
|
* cdk_salloc:
|
|
* @size: how much bytes should be allocated.
|
|
* @clear: shall the buffer cleared after the allocation?
|
|
*
|
|
* Allocated the requested amount of bytes in 'secure' memory.
|
|
*/
|
|
void *
|
|
cdk_salloc (size_t size, int clear)
|
|
{
|
|
void *p;
|
|
|
|
if (!secmem_init)
|
|
_secmem_init (SECMEM_SIZE);
|
|
|
|
p = alloc_secure_func (size);
|
|
if (!p)
|
|
out_of_core (size);
|
|
if (clear)
|
|
memset (p, 0, size);
|
|
return p;
|
|
}
|
|
|
|
|
|
void *
|
|
cdk_realloc (void *ptr, size_t size)
|
|
{
|
|
void *p = realloc_func (ptr, size);
|
|
if (!p)
|
|
out_of_core (size);
|
|
return p;
|
|
}
|
|
|
|
|
|
char *
|
|
cdk_strdup (const char *ptr)
|
|
{
|
|
char *p = cdk_malloc (strlen (ptr) + 1);
|
|
if (p)
|
|
strcpy (p, ptr);
|
|
return p;
|
|
}
|
|
|
|
|
|
void
|
|
cdk_free (void *ptr)
|
|
{
|
|
if (ptr)
|
|
free_func (ptr);
|
|
}
|
|
|
|
|
|
/* Internal logging routine. */
|
|
static void
|
|
_cdk_logv (int level, const char *fmt, va_list arg_ptr)
|
|
{
|
|
|
|
if (log_handler)
|
|
log_handler (log_handler_value, level, fmt, arg_ptr);
|
|
else
|
|
{
|
|
if (level == CDK_LOG_NONE)
|
|
return;
|
|
if (level == CDK_LOG_DEBUG)
|
|
fputs ("DBG: ", stderr);
|
|
vfprintf (stderr, fmt, arg_ptr);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* cdk_set_log_handler:
|
|
* @logfnc: the function pointer
|
|
* @opaque: a private values for the function
|
|
*
|
|
* Set a custom handler for logging.
|
|
**/
|
|
void
|
|
cdk_set_log_handler (cdk_log_fnc_t logfnc, void *opaque)
|
|
{
|
|
log_handler = logfnc;
|
|
log_handler_value = opaque;
|
|
}
|
|
|
|
|
|
/**
|
|
* cdk_set_log_level:
|
|
* @lvl: the level
|
|
*
|
|
* Set the verbosity level.
|
|
**/
|
|
void
|
|
cdk_set_log_level (int level)
|
|
{
|
|
log_level = level;
|
|
}
|
|
|
|
|
|
/* Return the current log level of the lib. */
|
|
int
|
|
_cdk_get_log_level (void)
|
|
{
|
|
return log_level;
|
|
}
|
|
|
|
|
|
void
|
|
_cdk_log_info (const char *fmt, ...)
|
|
{
|
|
va_list arg;
|
|
|
|
if (log_level == CDK_LOG_NONE)
|
|
return;
|
|
va_start (arg, fmt);
|
|
_cdk_logv (CDK_LOG_INFO, fmt, arg);
|
|
va_end (arg);
|
|
}
|
|
|
|
|
|
void
|
|
_cdk_log_debug (const char *fmt, ...)
|
|
{
|
|
va_list arg;
|
|
|
|
if (log_level < CDK_LOG_DEBUG)
|
|
return;
|
|
va_start (arg, fmt);
|
|
_cdk_logv (CDK_LOG_DEBUG, fmt, arg);
|
|
va_end (arg);
|
|
}
|
|
|
|
|
|
/* Use the passphrase callback in the handle HD or
|
|
return NULL if there is no valid callback. */
|
|
char *
|
|
_cdk_passphrase_get (cdk_ctx_t hd, const char *prompt)
|
|
{
|
|
if (!hd || !hd->passphrase_cb)
|
|
return NULL;
|
|
return hd->passphrase_cb (hd->passphrase_cb_value, prompt);
|
|
}
|
|
|
|
|
|
static void
|
|
handle_set_cipher (cdk_ctx_t hd, int cipher)
|
|
{
|
|
if (!hd)
|
|
return;
|
|
if (gcry_cipher_test_algo (cipher))
|
|
cipher = DEFAULT_CIPHER_ALGO;
|
|
hd->cipher_algo = cipher;
|
|
}
|
|
|
|
|
|
static void
|
|
handle_set_digest (cdk_ctx_t hd, int digest)
|
|
{
|
|
if (!hd)
|
|
return;
|
|
if (gcry_md_test_algo (digest))
|
|
digest = DEFAULT_DIGEST_ALGO;
|
|
hd->digest_algo = digest;
|
|
}
|
|
|
|
|
|
static void
|
|
handle_set_s2k (cdk_ctx_t hd, int mode, int digest, int cipher)
|
|
{
|
|
if (!hd)
|
|
return;
|
|
if (gcry_cipher_test_algo (cipher))
|
|
cipher = DEFAULT_CIPHER_ALGO;
|
|
if (gcry_md_test_algo (digest))
|
|
digest = DEFAULT_DIGEST_ALGO;
|
|
if (mode != CDK_S2K_SIMPLE &&
|
|
mode != CDK_S2K_SALTED && mode != CDK_S2K_ITERSALTED)
|
|
mode = CDK_S2K_ITERSALTED;
|
|
hd->_s2k.mode = mode;
|
|
hd->_s2k.digest_algo = digest;
|
|
}
|
|
|
|
|
|
static void
|
|
handle_set_compress (cdk_ctx_t hd, int algo, int level)
|
|
{
|
|
if (!hd)
|
|
return;
|
|
if (algo < 0 || algo > 2)
|
|
algo = 0;
|
|
hd->compress.algo = algo;
|
|
if (!algo)
|
|
hd->opt.compress = 0;
|
|
else
|
|
{
|
|
if (level > 0 && level < 10)
|
|
hd->compress.level = level;
|
|
else
|
|
hd->compress.level = 6;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* cdk_handle_control:
|
|
* @hd: session handle
|
|
* @action: flag which indicates whether put or get is requested
|
|
* @cmd: command id
|
|
*
|
|
* Perform various control operations for the current session.
|
|
**/
|
|
int
|
|
cdk_handle_control (cdk_ctx_t hd, int action, int cmd, ...)
|
|
{
|
|
va_list arg_ptr;
|
|
int set = action == CDK_CTLF_SET, val = 0;
|
|
|
|
if (!hd)
|
|
return -1;
|
|
|
|
if (action != CDK_CTLF_SET && action != CDK_CTLF_GET)
|
|
return -1;
|
|
va_start (arg_ptr, cmd);
|
|
switch (cmd)
|
|
{
|
|
case CDK_CTL_ARMOR:
|
|
if (set)
|
|
hd->opt.armor = va_arg (arg_ptr, int);
|
|
else
|
|
val = hd->opt.armor;
|
|
break;
|
|
|
|
case CDK_CTL_CIPHER:
|
|
if (set)
|
|
handle_set_cipher (hd, va_arg (arg_ptr, int));
|
|
else
|
|
val = hd->cipher_algo;
|
|
break;
|
|
|
|
case CDK_CTL_DIGEST:
|
|
if (set)
|
|
handle_set_digest (hd, va_arg (arg_ptr, int));
|
|
else
|
|
val = hd->digest_algo;
|
|
break;
|
|
|
|
case CDK_CTL_OVERWRITE:
|
|
if (set)
|
|
hd->opt.overwrite = va_arg (arg_ptr, int);
|
|
else
|
|
val = hd->opt.overwrite;
|
|
break;
|
|
|
|
case CDK_CTL_COMPRESS:
|
|
if (set)
|
|
{
|
|
int algo = va_arg (arg_ptr, int);
|
|
int level = va_arg (arg_ptr, int);
|
|
handle_set_compress (hd, algo, level);
|
|
}
|
|
else
|
|
val = hd->compress.algo;
|
|
break;
|
|
|
|
case CDK_CTL_S2K:
|
|
if (set)
|
|
{
|
|
int mode = va_arg (arg_ptr, int);
|
|
int digest = va_arg (arg_ptr, int);
|
|
int cipher = va_arg (arg_ptr, int);
|
|
handle_set_s2k (hd, mode, digest, cipher);
|
|
}
|
|
else
|
|
val = hd->_s2k.mode;
|
|
break;
|
|
|
|
case CDK_CTL_FORCE_DIGEST:
|
|
if (set)
|
|
hd->opt.force_digest = va_arg (arg_ptr, int);
|
|
else
|
|
val = hd->opt.force_digest;
|
|
break;
|
|
|
|
case CDK_CTL_BLOCKMODE_ON:
|
|
if (set)
|
|
hd->opt.blockmode = va_arg (arg_ptr, int);
|
|
else
|
|
val = hd->opt.blockmode;
|
|
break;
|
|
|
|
default:
|
|
val = -1;
|
|
break;
|
|
}
|
|
va_end (arg_ptr);
|
|
return val;
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* cdk_handle_new:
|
|
* @r_ctx: context to store the handle
|
|
*
|
|
* create a new session handle.
|
|
**/
|
|
cdk_error_t
|
|
cdk_handle_new (cdk_ctx_t * r_ctx)
|
|
{
|
|
cdk_ctx_t c;
|
|
|
|
if (!r_ctx)
|
|
return CDK_Inv_Value;
|
|
|
|
c = cdk_calloc (1, sizeof *c);
|
|
if (!c)
|
|
return CDK_Out_Of_Core;
|
|
|
|
/* For S2K use the iterated and salted mode and use the
|
|
default digest and cipher algorithms. Because the MDC
|
|
feature will be used, the default cipher should use a
|
|
blocksize of 128 bits. */
|
|
c->_s2k.mode = CDK_S2K_ITERSALTED;
|
|
c->_s2k.digest_algo = DEFAULT_DIGEST_ALGO;
|
|
|
|
c->opt.mdc = 1;
|
|
c->opt.compress = 1;
|
|
c->opt.armor = 0;
|
|
c->opt.textmode = 0;
|
|
|
|
c->digest_algo = DEFAULT_DIGEST_ALGO;
|
|
c->cipher_algo = DEFAULT_CIPHER_ALGO;
|
|
|
|
c->compress.algo = CDK_COMPRESS_ZIP;
|
|
c->compress.level = 6;
|
|
|
|
*r_ctx = c;
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* cdk_handle_set_keyring:
|
|
* @hd: session handle
|
|
* @type: public=0 or secret=1 keyring type
|
|
* @kringname: file name of the keyring which shall be used.
|
|
*
|
|
* Convenient function to set the keyring for the current session.
|
|
*/
|
|
cdk_error_t
|
|
cdk_handle_set_keyring (cdk_ctx_t hd, int type, const char *kringname)
|
|
{
|
|
cdk_keydb_hd_t db;
|
|
cdk_error_t err;
|
|
|
|
err = cdk_keydb_new_from_file (&db, type, kringname);
|
|
if (err)
|
|
return err;
|
|
|
|
if (!type)
|
|
hd->db.pub = db;
|
|
else
|
|
hd->db.sec = db;
|
|
hd->db.close_db = 1;
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* cdk_handle_set_keydb:
|
|
* @hd: session handle
|
|
* @db: the database handle
|
|
*
|
|
* set the key database handle.
|
|
* the function automatically detects whether this is a public or
|
|
* secret keyring and the right handle is set.
|
|
**/
|
|
void
|
|
cdk_handle_set_keydb (cdk_ctx_t hd, cdk_keydb_hd_t db)
|
|
{
|
|
if (!hd)
|
|
return;
|
|
if (_cdk_keydb_is_secret (db))
|
|
hd->db.sec = db;
|
|
else
|
|
hd->db.pub = db;
|
|
}
|
|
|
|
|
|
/**
|
|
* cdk_handle_get_keydb:
|
|
* @hd: session handle
|
|
* @type: type of the keyring
|
|
*
|
|
* Return the keydb handle from the session handle.
|
|
* The caller should not free these handles.
|
|
**/
|
|
cdk_keydb_hd_t
|
|
cdk_handle_get_keydb (cdk_ctx_t hd, int type)
|
|
{
|
|
if (!hd)
|
|
return NULL;
|
|
if (type == CDK_DBTYPE_PK_KEYRING)
|
|
return hd->db.pub;
|
|
else if (type == CDK_DBTYPE_SK_KEYRING)
|
|
return hd->db.sec;
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/**
|
|
* cdk_handle_set_passphrase_cb:
|
|
* @hd: session handle
|
|
* @cb: callback function
|
|
* @cb_value: the opaque value for the cb function
|
|
*
|
|
* set the passphrase callback.
|
|
**/
|
|
void
|
|
cdk_handle_set_passphrase_cb (cdk_ctx_t hd,
|
|
char *(*cb) (void *opa, const char *prompt),
|
|
void *cb_value)
|
|
{
|
|
if (!hd)
|
|
return;
|
|
hd->passphrase_cb = cb;
|
|
hd->passphrase_cb_value = cb_value;
|
|
}
|
|
|
|
|
|
/**
|
|
* cdk_handle_verify_get_result:
|
|
* @hd: the session handle
|
|
*
|
|
* Return the verify result for the current session.
|
|
* Do not free the pointer.
|
|
**/
|
|
cdk_verify_result_t
|
|
cdk_handle_verify_get_result (cdk_ctx_t hd)
|
|
{
|
|
return hd->result.verify;
|
|
}
|
|
|
|
|
|
/**
|
|
* cdk_handle_free:
|
|
* @hd: the handle
|
|
*
|
|
* Release the main handle.
|
|
**/
|
|
void
|
|
cdk_handle_free (cdk_ctx_t hd)
|
|
{
|
|
if (!hd)
|
|
return;
|
|
_cdk_result_verify_free (hd->result.verify);
|
|
|
|
/* If cdk_handle_set_keyring() were used, we need to free the key db
|
|
handles here because the handles are not controlled by the user. */
|
|
if (hd->db.close_db)
|
|
{
|
|
if (hd->db.pub)
|
|
cdk_keydb_free (hd->db.pub);
|
|
if (hd->db.sec)
|
|
cdk_keydb_free (hd->db.sec);
|
|
hd->db.pub = hd->db.sec = NULL;
|
|
}
|
|
cdk_free (hd->dek);
|
|
cdk_free (hd);
|
|
}
|