mirror of
git://git.gnupg.org/gnupg.git
synced 2025-02-01 16:33:02 +01:00
keyboxd: Restructure client access code.
* kbx/kbx-client-util.c: New. * kbx/kbx-client-util.h: New. * kbx/Makefile.am (client_sources): New. * g10/keydb.c (parse_keyblock_image): Rename to keydb_parse_keyblock and make global. * g10/call-keyboxd.c: Include kbx-client-util.h. (struct keyboxd_local_s): Remove struct datastream. Add field kcd. Remove per_session_init_done. (lock_datastream, unlock_datastream): Remove. (prepare_data_pipe, datastream_thread): Remove. (keydb_get_keyblock_do_parse): Remove. (gpg_keyboxd_deinit_session_data): Release the KCD object. (open_context): Use of kbx_client_data_new. (keydb_get_keyblock): Simplify. (keydb_search): Use kbx_client_data_cmd and _wait. -- The data specific part of the code has been moved from gpg to a new module in kbx/ so that it can also be used by gpgsm. The OpenPGP parsing while reading the data has been replaced by storing the data in memory and parse it later. That makes a nice interface and abstracts the fd-passing/D-lines handling away. Signed-off-by: Werner Koch <wk@gnupg.org>
This commit is contained in:
parent
4d839f5a80
commit
497db0b5bc
@ -40,6 +40,7 @@
|
|||||||
#include "../common/host2net.h"
|
#include "../common/host2net.h"
|
||||||
#include "../common/exechelp.h"
|
#include "../common/exechelp.h"
|
||||||
#include "../common/status.h"
|
#include "../common/status.h"
|
||||||
|
#include "../kbx/kbx-client-util.h"
|
||||||
#include "keydb.h"
|
#include "keydb.h"
|
||||||
|
|
||||||
#include "keydb-private.h" /* For struct keydb_handle_s */
|
#include "keydb-private.h" /* For struct keydb_handle_s */
|
||||||
@ -57,20 +58,8 @@ struct keyboxd_local_s
|
|||||||
/* The active Assuan context. */
|
/* The active Assuan context. */
|
||||||
assuan_context_t ctx;
|
assuan_context_t ctx;
|
||||||
|
|
||||||
/* This object is used if fd-passing is used to convey the
|
/* The client data helper context. */
|
||||||
* keyblocks. */
|
kbx_client_data_t kcd;
|
||||||
struct {
|
|
||||||
/* NULL or a stream used to receive data. */
|
|
||||||
estream_t fp;
|
|
||||||
|
|
||||||
/* Condition variable to sync the datastream with the command. */
|
|
||||||
npth_mutex_t mutex;
|
|
||||||
npth_cond_t cond;
|
|
||||||
|
|
||||||
/* The found keyblock or the parsing error. */
|
|
||||||
kbnode_t found_keyblock;
|
|
||||||
gpg_error_t found_err;
|
|
||||||
} datastream;
|
|
||||||
|
|
||||||
/* I/O buffer with the last search result or NULL. Used if
|
/* I/O buffer with the last search result or NULL. Used if
|
||||||
* D-lines are used to convey the keyblocks. */
|
* D-lines are used to convey the keyblocks. */
|
||||||
@ -79,41 +68,14 @@ struct keyboxd_local_s
|
|||||||
/* This flag set while an operation is running on this context. */
|
/* This flag set while an operation is running on this context. */
|
||||||
unsigned int is_active : 1;
|
unsigned int is_active : 1;
|
||||||
|
|
||||||
/* This flag is set to record that the standard per session init has
|
|
||||||
* been done. */
|
|
||||||
unsigned int per_session_init_done : 1;
|
|
||||||
|
|
||||||
/* Flag indicating that a search reset is required. */
|
/* Flag indicating that a search reset is required. */
|
||||||
unsigned int need_search_reset : 1;
|
unsigned int need_search_reset : 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/* Local prototypes. */
|
|
||||||
static void *datastream_thread (void *arg);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static void
|
|
||||||
lock_datastream (keyboxd_local_t kbl)
|
|
||||||
{
|
|
||||||
int rc = npth_mutex_lock (&kbl->datastream.mutex);
|
|
||||||
if (rc)
|
|
||||||
log_fatal ("%s: failed to acquire mutex: %s\n", __func__,
|
|
||||||
gpg_strerror (gpg_error_from_errno (rc)));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void
|
|
||||||
unlock_datastream (keyboxd_local_t kbl)
|
|
||||||
{
|
|
||||||
int rc = npth_mutex_unlock (&kbl->datastream.mutex);
|
|
||||||
if (rc)
|
|
||||||
log_fatal ("%s: failed to release mutex: %s\n", __func__,
|
|
||||||
gpg_strerror (gpg_error_from_errno (rc)));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Deinitialize all session resources pertaining to the keyboxd. */
|
/* Deinitialize all session resources pertaining to the keyboxd. */
|
||||||
void
|
void
|
||||||
gpg_keyboxd_deinit_session_data (ctrl_t ctrl)
|
gpg_keyboxd_deinit_session_data (ctrl_t ctrl)
|
||||||
@ -127,8 +89,8 @@ gpg_keyboxd_deinit_session_data (ctrl_t ctrl)
|
|||||||
log_error ("oops: trying to cleanup an active keyboxd context\n");
|
log_error ("oops: trying to cleanup an active keyboxd context\n");
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
es_fclose (kbl->datastream.fp);
|
kbx_client_data_release (kbl->kcd);
|
||||||
kbl->datastream.fp = NULL;
|
kbl->kcd = NULL;
|
||||||
assuan_release (kbl->ctx);
|
assuan_release (kbl->ctx);
|
||||||
kbl->ctx = NULL;
|
kbl->ctx = NULL;
|
||||||
}
|
}
|
||||||
@ -189,73 +151,6 @@ create_new_context (ctrl_t ctrl, assuan_context_t *r_ctx)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Setup the pipe used for receiving data from the keyboxd. Store the
|
|
||||||
* info on KBL. */
|
|
||||||
static gpg_error_t
|
|
||||||
prepare_data_pipe (keyboxd_local_t kbl)
|
|
||||||
{
|
|
||||||
gpg_error_t err;
|
|
||||||
int rc;
|
|
||||||
int inpipe[2];
|
|
||||||
estream_t infp;
|
|
||||||
npth_t thread;
|
|
||||||
npth_attr_t tattr;
|
|
||||||
|
|
||||||
err = gnupg_create_inbound_pipe (inpipe, &infp, 0);
|
|
||||||
if (err)
|
|
||||||
{
|
|
||||||
log_error ("error creating inbound pipe: %s\n", gpg_strerror (err));
|
|
||||||
return err; /* That should not happen. */
|
|
||||||
}
|
|
||||||
|
|
||||||
err = assuan_sendfd (kbl->ctx, INT2FD (inpipe[1]));
|
|
||||||
if (err)
|
|
||||||
{
|
|
||||||
log_error ("sending sending fd %d to keyboxd: %s <%s>\n",
|
|
||||||
inpipe[1], gpg_strerror (err), gpg_strsource (err));
|
|
||||||
es_fclose (infp);
|
|
||||||
close (inpipe[1]);
|
|
||||||
return 0; /* Server may not support fd-passing. */
|
|
||||||
}
|
|
||||||
|
|
||||||
err = assuan_transact (kbl->ctx, "OUTPUT FD",
|
|
||||||
NULL, NULL, NULL, NULL, NULL, NULL);
|
|
||||||
if (err)
|
|
||||||
{
|
|
||||||
log_info ("keyboxd does not accept our fd: %s <%s>\n",
|
|
||||||
gpg_strerror (err), gpg_strsource (err));
|
|
||||||
es_fclose (infp);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
kbl->datastream.fp = infp;
|
|
||||||
kbl->datastream.found_keyblock = NULL;
|
|
||||||
kbl->datastream.found_err = 0;
|
|
||||||
|
|
||||||
rc = npth_attr_init (&tattr);
|
|
||||||
if (rc)
|
|
||||||
{
|
|
||||||
err = gpg_error_from_errno (rc);
|
|
||||||
log_error ("error preparing thread for keyboxd: %s\n",gpg_strerror (err));
|
|
||||||
es_fclose (infp);
|
|
||||||
kbl->datastream.fp = NULL;
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
npth_attr_setdetachstate (&tattr, NPTH_CREATE_DETACHED);
|
|
||||||
rc = npth_create (&thread, &tattr, datastream_thread, kbl);
|
|
||||||
if (rc)
|
|
||||||
{
|
|
||||||
err = gpg_error_from_errno (rc);
|
|
||||||
log_error ("error spawning thread for keyboxd: %s\n", gpg_strerror (err));
|
|
||||||
npth_attr_destroy (&tattr);
|
|
||||||
es_fclose (infp);
|
|
||||||
kbl->datastream.fp = NULL;
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Get a context for accessing keyboxd. If no context is available a
|
/* Get a context for accessing keyboxd. If no context is available a
|
||||||
* new one is created and if necessary keyboxd is started. R_KBL
|
* new one is created and if necessary keyboxd is started. R_KBL
|
||||||
@ -264,7 +159,6 @@ static gpg_error_t
|
|||||||
open_context (ctrl_t ctrl, keyboxd_local_t *r_kbl)
|
open_context (ctrl_t ctrl, keyboxd_local_t *r_kbl)
|
||||||
{
|
{
|
||||||
gpg_error_t err;
|
gpg_error_t err;
|
||||||
int rc;
|
|
||||||
keyboxd_local_t kbl;
|
keyboxd_local_t kbl;
|
||||||
|
|
||||||
*r_kbl = NULL;
|
*r_kbl = NULL;
|
||||||
@ -277,15 +171,6 @@ open_context (ctrl_t ctrl, keyboxd_local_t *r_kbl)
|
|||||||
/* Found an inactive keyboxd session - return that. */
|
/* Found an inactive keyboxd session - return that. */
|
||||||
log_assert (!kbl->is_active);
|
log_assert (!kbl->is_active);
|
||||||
|
|
||||||
/* But first do the per session init if not yet done. */
|
|
||||||
if (!kbl->per_session_init_done)
|
|
||||||
{
|
|
||||||
err = prepare_data_pipe (kbl);
|
|
||||||
if (err)
|
|
||||||
return err;
|
|
||||||
kbl->per_session_init_done = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
kbl->is_active = 1;
|
kbl->is_active = 1;
|
||||||
kbl->need_search_reset = 1;
|
kbl->need_search_reset = 1;
|
||||||
|
|
||||||
@ -298,29 +183,17 @@ open_context (ctrl_t ctrl, keyboxd_local_t *r_kbl)
|
|||||||
if (!kbl)
|
if (!kbl)
|
||||||
return gpg_error_from_syserror ();
|
return gpg_error_from_syserror ();
|
||||||
|
|
||||||
rc = npth_mutex_init (&kbl->datastream.mutex, NULL);
|
err = create_new_context (ctrl, &kbl->ctx);
|
||||||
if (rc)
|
if (err)
|
||||||
{
|
{
|
||||||
err = gpg_error_from_errno (rc);
|
|
||||||
log_error ("error initializing mutex: %s\n", gpg_strerror (err));
|
|
||||||
xfree (kbl);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
rc = npth_cond_init (&kbl->datastream.cond, NULL);
|
|
||||||
if (rc)
|
|
||||||
{
|
|
||||||
err = gpg_error_from_errno (rc);
|
|
||||||
log_error ("error initializing condition: %s\n", gpg_strerror (err));
|
|
||||||
npth_mutex_destroy (&kbl->datastream.mutex);
|
|
||||||
xfree (kbl);
|
xfree (kbl);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = create_new_context (ctrl, &kbl->ctx);
|
err = kbx_client_data_new (&kbl->kcd, kbl->ctx);
|
||||||
if (err)
|
if (err)
|
||||||
{
|
{
|
||||||
npth_cond_destroy (&kbl->datastream.cond);
|
assuan_release (kbl->ctx);
|
||||||
npth_mutex_destroy (&kbl->datastream.mutex);
|
|
||||||
xfree (kbl);
|
xfree (kbl);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
@ -427,224 +300,6 @@ keydb_lock (KEYDB_HANDLE hd)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* FIXME: This helper is duplicates code of partse_keyblock_image. */
|
|
||||||
static gpg_error_t
|
|
||||||
keydb_get_keyblock_do_parse (iobuf_t iobuf, int pk_no, int uid_no,
|
|
||||||
kbnode_t *r_keyblock)
|
|
||||||
{
|
|
||||||
gpg_error_t err;
|
|
||||||
struct parse_packet_ctx_s parsectx;
|
|
||||||
PACKET *pkt;
|
|
||||||
kbnode_t keyblock = NULL;
|
|
||||||
kbnode_t node, *tail;
|
|
||||||
int in_cert, save_mode;
|
|
||||||
int pk_count, uid_count;
|
|
||||||
|
|
||||||
*r_keyblock = NULL;
|
|
||||||
|
|
||||||
pkt = xtrymalloc (sizeof *pkt);
|
|
||||||
if (!pkt)
|
|
||||||
return gpg_error_from_syserror ();
|
|
||||||
init_packet (pkt);
|
|
||||||
init_parse_packet (&parsectx, iobuf);
|
|
||||||
save_mode = set_packet_list_mode (0);
|
|
||||||
in_cert = 0;
|
|
||||||
tail = NULL;
|
|
||||||
pk_count = uid_count = 0;
|
|
||||||
while ((err = parse_packet (&parsectx, pkt)) != -1)
|
|
||||||
{
|
|
||||||
if (gpg_err_code (err) == GPG_ERR_UNKNOWN_PACKET)
|
|
||||||
{
|
|
||||||
free_packet (pkt, &parsectx);
|
|
||||||
init_packet (pkt);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (err)
|
|
||||||
{
|
|
||||||
es_fflush (es_stdout);
|
|
||||||
log_error ("parse_keyblock_image: read error: %s\n",
|
|
||||||
gpg_strerror (err));
|
|
||||||
if (gpg_err_code (err) == GPG_ERR_INV_PACKET)
|
|
||||||
{
|
|
||||||
free_packet (pkt, &parsectx);
|
|
||||||
init_packet (pkt);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
err = gpg_error (GPG_ERR_INV_KEYRING);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Filter allowed packets. */
|
|
||||||
switch (pkt->pkttype)
|
|
||||||
{
|
|
||||||
case PKT_PUBLIC_KEY:
|
|
||||||
case PKT_PUBLIC_SUBKEY:
|
|
||||||
case PKT_SECRET_KEY:
|
|
||||||
case PKT_SECRET_SUBKEY:
|
|
||||||
case PKT_USER_ID:
|
|
||||||
case PKT_ATTRIBUTE:
|
|
||||||
case PKT_SIGNATURE:
|
|
||||||
case PKT_RING_TRUST:
|
|
||||||
break; /* Allowed per RFC. */
|
|
||||||
|
|
||||||
default:
|
|
||||||
log_info ("skipped packet of type %d in keybox\n", (int)pkt->pkttype);
|
|
||||||
free_packet(pkt, &parsectx);
|
|
||||||
init_packet(pkt);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Other sanity checks. */
|
|
||||||
if (!in_cert && pkt->pkttype != PKT_PUBLIC_KEY)
|
|
||||||
{
|
|
||||||
log_error ("parse_keyblock_image: first packet in a keybox blob "
|
|
||||||
"is not a public key packet\n");
|
|
||||||
err = gpg_error (GPG_ERR_INV_KEYRING);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (in_cert && (pkt->pkttype == PKT_PUBLIC_KEY
|
|
||||||
|| pkt->pkttype == PKT_SECRET_KEY))
|
|
||||||
{
|
|
||||||
log_error ("parse_keyblock_image: "
|
|
||||||
"multiple keyblocks in a keybox blob\n");
|
|
||||||
err = gpg_error (GPG_ERR_INV_KEYRING);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
in_cert = 1;
|
|
||||||
|
|
||||||
node = new_kbnode (pkt);
|
|
||||||
|
|
||||||
switch (pkt->pkttype)
|
|
||||||
{
|
|
||||||
case PKT_PUBLIC_KEY:
|
|
||||||
case PKT_PUBLIC_SUBKEY:
|
|
||||||
case PKT_SECRET_KEY:
|
|
||||||
case PKT_SECRET_SUBKEY:
|
|
||||||
if (++pk_count == pk_no)
|
|
||||||
node->flag |= 1;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PKT_USER_ID:
|
|
||||||
if (++uid_count == uid_no)
|
|
||||||
node->flag |= 2;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!keyblock)
|
|
||||||
keyblock = node;
|
|
||||||
else
|
|
||||||
*tail = node;
|
|
||||||
tail = &node->next;
|
|
||||||
pkt = xtrymalloc (sizeof *pkt);
|
|
||||||
if (!pkt)
|
|
||||||
{
|
|
||||||
err = gpg_error_from_syserror ();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
init_packet (pkt);
|
|
||||||
}
|
|
||||||
set_packet_list_mode (save_mode);
|
|
||||||
|
|
||||||
if (err == -1 && keyblock)
|
|
||||||
err = 0; /* Got the entire keyblock. */
|
|
||||||
|
|
||||||
if (err)
|
|
||||||
release_kbnode (keyblock);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
*r_keyblock = keyblock;
|
|
||||||
}
|
|
||||||
free_packet (pkt, &parsectx);
|
|
||||||
deinit_parse_packet (&parsectx);
|
|
||||||
xfree (pkt);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* The thread used to read from the data stream. This is running as
|
|
||||||
* long as the connection and its datastream exists. */
|
|
||||||
static void *
|
|
||||||
datastream_thread (void *arg)
|
|
||||||
{
|
|
||||||
keyboxd_local_t kbl = arg;
|
|
||||||
gpg_error_t err;
|
|
||||||
int rc;
|
|
||||||
unsigned char lenbuf[4];
|
|
||||||
size_t nread, datalen;
|
|
||||||
iobuf_t iobuf;
|
|
||||||
int pk_no, uid_no;
|
|
||||||
kbnode_t keyblock, tmpkeyblock;
|
|
||||||
|
|
||||||
|
|
||||||
log_debug ("Datastream_thread started\n");
|
|
||||||
while (kbl->datastream.fp)
|
|
||||||
{
|
|
||||||
/* log_debug ("Datastream_thread waiting ...\n"); */
|
|
||||||
if (es_read (kbl->datastream.fp, lenbuf, 4, &nread))
|
|
||||||
{
|
|
||||||
err = gpg_error_from_syserror ();
|
|
||||||
if (gpg_err_code (err) == GPG_ERR_EAGAIN)
|
|
||||||
continue;
|
|
||||||
log_error ("error reading data length from keyboxd: %s\n",
|
|
||||||
gpg_strerror (err));
|
|
||||||
gnupg_sleep (1);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (nread != 4)
|
|
||||||
{
|
|
||||||
err = gpg_error (GPG_ERR_EIO);
|
|
||||||
log_error ("error reading data length from keyboxd: %s\n",
|
|
||||||
"short read");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
datalen = buf32_to_size_t (lenbuf);
|
|
||||||
/* log_debug ("keyboxd announced %zu bytes\n", datalen); */
|
|
||||||
|
|
||||||
iobuf = iobuf_esopen (kbl->datastream.fp, "rb", 1, datalen);
|
|
||||||
pk_no = uid_no = 0; /* FIXME: Get this from the keyboxd. */
|
|
||||||
err = keydb_get_keyblock_do_parse (iobuf, pk_no, uid_no, &keyblock);
|
|
||||||
iobuf_close (iobuf);
|
|
||||||
if (!err)
|
|
||||||
{
|
|
||||||
/* log_debug ("parsing datastream succeeded\n"); */
|
|
||||||
|
|
||||||
/* Thread-safe assignment to the result var: */
|
|
||||||
tmpkeyblock = kbl->datastream.found_keyblock;
|
|
||||||
kbl->datastream.found_keyblock = keyblock;
|
|
||||||
release_kbnode (tmpkeyblock);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* log_debug ("parsing datastream failed: %s <%s>\n", */
|
|
||||||
/* gpg_strerror (err), gpg_strsource (err)); */
|
|
||||||
tmpkeyblock = kbl->datastream.found_keyblock;
|
|
||||||
kbl->datastream.found_keyblock = NULL;
|
|
||||||
kbl->datastream.found_err = err;
|
|
||||||
release_kbnode (tmpkeyblock);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Tell the main thread. */
|
|
||||||
lock_datastream (kbl);
|
|
||||||
rc = npth_cond_signal (&kbl->datastream.cond);
|
|
||||||
if (rc)
|
|
||||||
{
|
|
||||||
err = gpg_error_from_errno (rc);
|
|
||||||
log_error ("%s: signaling condition failed: %s\n",
|
|
||||||
__func__, gpg_strerror (err));
|
|
||||||
}
|
|
||||||
unlock_datastream (kbl);
|
|
||||||
}
|
|
||||||
log_debug ("Datastream_thread finished\n");
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Return the keyblock last found by keydb_search() in *RET_KB.
|
/* Return the keyblock last found by keydb_search() in *RET_KB.
|
||||||
*
|
*
|
||||||
* On success, the function returns 0 and the caller must free *RET_KB
|
* On success, the function returns 0 and the caller must free *RET_KB
|
||||||
@ -677,19 +332,13 @@ keydb_get_keyblock (KEYDB_HANDLE hd, kbnode_t *ret_kb)
|
|||||||
if (hd->kbl->search_result)
|
if (hd->kbl->search_result)
|
||||||
{
|
{
|
||||||
pk_no = uid_no = 0; /*FIXME: Get this from the keyboxd. */
|
pk_no = uid_no = 0; /*FIXME: Get this from the keyboxd. */
|
||||||
err = keydb_get_keyblock_do_parse (hd->kbl->search_result,
|
err = keydb_parse_keyblock (hd->kbl->search_result, pk_no, uid_no,
|
||||||
pk_no, uid_no, ret_kb);
|
ret_kb);
|
||||||
/* In contrast to the old code we close the iobuf here and thus
|
/* In contrast to the old code we close the iobuf here and thus
|
||||||
* this function may be called only once to get a keyblock. */
|
* this function may be called only once to get a keyblock. */
|
||||||
iobuf_close (hd->kbl->search_result);
|
iobuf_close (hd->kbl->search_result);
|
||||||
hd->kbl->search_result = NULL;
|
hd->kbl->search_result = NULL;
|
||||||
}
|
}
|
||||||
else if (hd->kbl->datastream.found_keyblock)
|
|
||||||
{
|
|
||||||
*ret_kb = hd->kbl->datastream.found_keyblock;
|
|
||||||
hd->kbl->datastream.found_keyblock = NULL;
|
|
||||||
err = 0;
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
err = gpg_error (GPG_ERR_VALUE_NOT_FOUND);
|
err = gpg_error (GPG_ERR_VALUE_NOT_FOUND);
|
||||||
@ -976,7 +625,8 @@ keydb_search (KEYDB_HANDLE hd, KEYDB_SEARCH_DESC *desc,
|
|||||||
gpg_error_t err;
|
gpg_error_t err;
|
||||||
int i;
|
int i;
|
||||||
char line[ASSUAN_LINELENGTH];
|
char line[ASSUAN_LINELENGTH];
|
||||||
|
char *buffer;
|
||||||
|
size_t len;
|
||||||
|
|
||||||
if (!hd)
|
if (!hd)
|
||||||
return gpg_error (GPG_ERR_INV_ARG);
|
return gpg_error (GPG_ERR_INV_ARG);
|
||||||
@ -989,7 +639,7 @@ keydb_search (KEYDB_HANDLE hd, KEYDB_SEARCH_DESC *desc,
|
|||||||
|
|
||||||
if (DBG_LOOKUP)
|
if (DBG_LOOKUP)
|
||||||
{
|
{
|
||||||
log_debug ("%s: %zd search descriptions:\n", __func__, ndesc);
|
log_debug ("%s: %zu search descriptions:\n", __func__, ndesc);
|
||||||
for (i = 0; i < ndesc; i ++)
|
for (i = 0; i < ndesc; i ++)
|
||||||
{
|
{
|
||||||
char *t = keydb_search_desc_dump (&desc[i]);
|
char *t = keydb_search_desc_dump (&desc[i]);
|
||||||
@ -1010,11 +660,6 @@ keydb_search (KEYDB_HANDLE hd, KEYDB_SEARCH_DESC *desc,
|
|||||||
iobuf_close (hd->kbl->search_result);
|
iobuf_close (hd->kbl->search_result);
|
||||||
hd->kbl->search_result = NULL;
|
hd->kbl->search_result = NULL;
|
||||||
}
|
}
|
||||||
if (hd->kbl->datastream.found_keyblock)
|
|
||||||
{
|
|
||||||
release_kbnode (hd->kbl->datastream.found_keyblock);
|
|
||||||
hd->kbl->datastream.found_keyblock = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check whether this is a NEXT search. */
|
/* Check whether this is a NEXT search. */
|
||||||
if (!hd->kbl->need_search_reset)
|
if (!hd->kbl->need_search_reset)
|
||||||
@ -1129,72 +774,14 @@ keydb_search (KEYDB_HANDLE hd, KEYDB_SEARCH_DESC *desc,
|
|||||||
|
|
||||||
do_search:
|
do_search:
|
||||||
hd->last_ubid_valid = 0;
|
hd->last_ubid_valid = 0;
|
||||||
if (hd->kbl->datastream.fp)
|
err = kbx_client_data_cmd (hd->kbl->kcd, line, search_status_cb, hd);
|
||||||
|
if (!err && !(err = kbx_client_data_wait (hd->kbl->kcd, &buffer, &len)))
|
||||||
{
|
{
|
||||||
/* log_debug ("Sending command '%s'\n", line); */
|
|
||||||
err = assuan_transact (hd->kbl->ctx, line,
|
|
||||||
NULL, NULL,
|
|
||||||
NULL, NULL,
|
|
||||||
search_status_cb, hd);
|
|
||||||
if (err)
|
|
||||||
{
|
|
||||||
/* log_debug ("Finished command with error: %s\n", gpg_strerror (err)); */
|
|
||||||
/* Fixme: On unexpected errors we need a way to cancel the
|
|
||||||
* data stream. Probably it will be best to close and
|
|
||||||
* reopen it. */
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
int rc;
|
|
||||||
|
|
||||||
/* log_debug ("Finished command .. telling data stream\n"); */
|
|
||||||
lock_datastream (hd->kbl);
|
|
||||||
if (!hd->kbl->datastream.found_keyblock)
|
|
||||||
{
|
|
||||||
/* log_debug ("%s: waiting on datastream_cond ...\n", __func__); */
|
|
||||||
rc = npth_cond_wait (&hd->kbl->datastream.cond,
|
|
||||||
&hd->kbl->datastream.mutex);
|
|
||||||
/* log_debug ("%s: waiting on datastream.cond done\n", __func__); */
|
|
||||||
if (rc)
|
|
||||||
{
|
|
||||||
err = gpg_error_from_errno (rc);
|
|
||||||
log_error ("%s: waiting on condition failed: %s\n",
|
|
||||||
__func__, gpg_strerror (err));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
unlock_datastream (hd->kbl);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else /* Slower D-line version if fd-passing was not successful. */
|
|
||||||
{
|
|
||||||
membuf_t data;
|
|
||||||
void *buffer;
|
|
||||||
size_t len;
|
|
||||||
|
|
||||||
init_membuf (&data, 8192);
|
|
||||||
err = assuan_transact (hd->kbl->ctx, line,
|
|
||||||
put_membuf_cb, &data,
|
|
||||||
NULL, NULL,
|
|
||||||
search_status_cb, hd);
|
|
||||||
if (err)
|
|
||||||
{
|
|
||||||
xfree (get_membuf (&data, &len));
|
|
||||||
goto leave;
|
|
||||||
}
|
|
||||||
|
|
||||||
buffer = get_membuf (&data, &len);
|
|
||||||
if (!buffer)
|
|
||||||
{
|
|
||||||
err = gpg_error_from_syserror ();
|
|
||||||
goto leave;
|
|
||||||
}
|
|
||||||
|
|
||||||
hd->kbl->search_result = iobuf_temp_with_content (buffer, len);
|
hd->kbl->search_result = iobuf_temp_with_content (buffer, len);
|
||||||
xfree (buffer);
|
xfree (buffer);
|
||||||
}
|
/* if (hd->last_ubid_valid) */
|
||||||
|
/* log_printhex (hd->last_ubid, 20, "found UBID:"); */
|
||||||
/* if (hd->last_ubid_valid) */
|
}
|
||||||
/* log_printhex (hd->last_ubid, 20, "found UBID:"); */
|
|
||||||
|
|
||||||
leave:
|
leave:
|
||||||
if (DBG_CLOCK)
|
if (DBG_CLOCK)
|
||||||
|
@ -154,6 +154,10 @@ struct keydb_handle_s
|
|||||||
|
|
||||||
/*-- keydb.c --*/
|
/*-- keydb.c --*/
|
||||||
|
|
||||||
|
|
||||||
|
gpg_error_t keydb_parse_keyblock (iobuf_t iobuf, int pk_no, int uid_no,
|
||||||
|
kbnode_t *r_keyblock);
|
||||||
|
|
||||||
/* These are the functions call-keyboxd diverts to if the keyboxd is
|
/* These are the functions call-keyboxd diverts to if the keyboxd is
|
||||||
* not used. */
|
* not used. */
|
||||||
|
|
||||||
|
@ -1136,8 +1136,9 @@ keydb_pop_found_state (KEYDB_HANDLE hd)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
static gpg_error_t
|
/* Parse the keyblock in IOBUF and return at R_KEYBLOCK. */
|
||||||
parse_keyblock_image (iobuf_t iobuf, int pk_no, int uid_no,
|
gpg_error_t
|
||||||
|
keydb_parse_keyblock (iobuf_t iobuf, int pk_no, int uid_no,
|
||||||
kbnode_t *r_keyblock)
|
kbnode_t *r_keyblock)
|
||||||
{
|
{
|
||||||
gpg_error_t err;
|
gpg_error_t err;
|
||||||
@ -1300,7 +1301,7 @@ internal_keydb_get_keyblock (KEYDB_HANDLE hd, KBNODE *ret_kb)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
err = parse_keyblock_image (hd->keyblock_cache.iobuf,
|
err = keydb_parse_keyblock (hd->keyblock_cache.iobuf,
|
||||||
hd->keyblock_cache.pk_no,
|
hd->keyblock_cache.pk_no,
|
||||||
hd->keyblock_cache.uid_no,
|
hd->keyblock_cache.uid_no,
|
||||||
ret_kb);
|
ret_kb);
|
||||||
@ -1332,7 +1333,7 @@ internal_keydb_get_keyblock (KEYDB_HANDLE hd, KBNODE *ret_kb)
|
|||||||
&iobuf, &pk_no, &uid_no);
|
&iobuf, &pk_no, &uid_no);
|
||||||
if (!err)
|
if (!err)
|
||||||
{
|
{
|
||||||
err = parse_keyblock_image (iobuf, pk_no, uid_no, ret_kb);
|
err = keydb_parse_keyblock (iobuf, pk_no, uid_no, ret_kb);
|
||||||
if (!err && hd->keyblock_cache.state == KEYBLOCK_CACHE_PREPARED)
|
if (!err && hd->keyblock_cache.state == KEYBLOCK_CACHE_PREPARED)
|
||||||
{
|
{
|
||||||
hd->keyblock_cache.state = KEYBLOCK_CACHE_FILLED;
|
hd->keyblock_cache.state = KEYBLOCK_CACHE_FILLED;
|
||||||
|
@ -57,9 +57,13 @@ common_sources = \
|
|||||||
keybox-openpgp.c \
|
keybox-openpgp.c \
|
||||||
keybox-dump.c
|
keybox-dump.c
|
||||||
|
|
||||||
|
client_sources = \
|
||||||
|
kbx-client-util.h \
|
||||||
|
kbx-client-util.c
|
||||||
|
|
||||||
libkeybox_a_SOURCES = $(common_sources)
|
|
||||||
libkeybox509_a_SOURCES = $(common_sources)
|
libkeybox_a_SOURCES = $(common_sources) $(client_sources)
|
||||||
|
libkeybox509_a_SOURCES = $(common_sources) $(client_sources)
|
||||||
|
|
||||||
libkeybox_a_CFLAGS = $(AM_CFLAGS)
|
libkeybox_a_CFLAGS = $(AM_CFLAGS)
|
||||||
libkeybox509_a_CFLAGS = $(AM_CFLAGS) -DKEYBOX_WITH_X509=1
|
libkeybox509_a_CFLAGS = $(AM_CFLAGS) -DKEYBOX_WITH_X509=1
|
||||||
|
450
kbx/kbx-client-util.c
Normal file
450
kbx/kbx-client-util.c
Normal file
@ -0,0 +1,450 @@
|
|||||||
|
/* kbx-client-util.c - Utility functions to implement a keyboxd client
|
||||||
|
* Copyright (C) 2020 g10 Code GmbH
|
||||||
|
*
|
||||||
|
* This file is part of GnuPG.
|
||||||
|
*
|
||||||
|
* GnuPG 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 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* GnuPG 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.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, see <https://www.gnu.org/licenses/>.
|
||||||
|
* SPDX-License-Identifier: GPL-3.0+
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <config.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <npth.h>
|
||||||
|
#include <assuan.h>
|
||||||
|
|
||||||
|
#include "../common/util.h"
|
||||||
|
#include "../common/membuf.h"
|
||||||
|
#include "../common/i18n.h"
|
||||||
|
#include "../common/asshelp.h"
|
||||||
|
#include "../common/exechelp.h"
|
||||||
|
#include "../common/sysutils.h"
|
||||||
|
#include "../common/host2net.h"
|
||||||
|
#include "kbx-client-util.h"
|
||||||
|
|
||||||
|
|
||||||
|
#define MAX_DATABLOB_SIZE (16*1024*1024)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* This object is used to implement a client to the keyboxd. */
|
||||||
|
struct kbx_client_data_s
|
||||||
|
{
|
||||||
|
/* The used assuan context. */
|
||||||
|
assuan_context_t ctx;
|
||||||
|
|
||||||
|
/* A stream used to receive data. If this is NULL D-lines are used
|
||||||
|
* to receive the data. */
|
||||||
|
estream_t fp;
|
||||||
|
|
||||||
|
/* Condition variable to sync the datastream with the command. */
|
||||||
|
npth_mutex_t mutex;
|
||||||
|
npth_cond_t cond;
|
||||||
|
|
||||||
|
/* The data received from the keyboxd and an error code if there was
|
||||||
|
* a problem (in which case DATA is also set to NULL. This is only
|
||||||
|
* used if FP is not NULL. */
|
||||||
|
char *data;
|
||||||
|
size_t datalen;
|
||||||
|
gpg_error_t dataerr;
|
||||||
|
|
||||||
|
/* Helper variables in case D-lines are used (FP is NULL) */
|
||||||
|
char *dlinedata;
|
||||||
|
size_t dlinedatalen;
|
||||||
|
gpg_error_t dlineerr;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static void *datastream_thread (void *arg);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
lock_datastream (kbx_client_data_t kcd)
|
||||||
|
{
|
||||||
|
int rc = npth_mutex_lock (&kcd->mutex);
|
||||||
|
if (rc)
|
||||||
|
log_fatal ("%s: failed to acquire mutex: %s\n", __func__,
|
||||||
|
gpg_strerror (gpg_error_from_errno (rc)));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
unlock_datastream (kbx_client_data_t kcd)
|
||||||
|
{
|
||||||
|
int rc = npth_mutex_unlock (&kcd->mutex);
|
||||||
|
if (rc)
|
||||||
|
log_fatal ("%s: failed to release mutex: %s\n", __func__,
|
||||||
|
gpg_strerror (gpg_error_from_errno (rc)));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Setup the pipe used for receiving data from the keyboxd. Store the
|
||||||
|
* info on KCD. */
|
||||||
|
static gpg_error_t
|
||||||
|
prepare_data_pipe (kbx_client_data_t kcd)
|
||||||
|
{
|
||||||
|
gpg_error_t err;
|
||||||
|
int rc;
|
||||||
|
int inpipe[2];
|
||||||
|
estream_t infp;
|
||||||
|
npth_t thread;
|
||||||
|
npth_attr_t tattr;
|
||||||
|
|
||||||
|
kcd->fp = NULL;
|
||||||
|
kcd->data = NULL;
|
||||||
|
kcd->datalen = 0;
|
||||||
|
kcd->dataerr = 0;
|
||||||
|
|
||||||
|
err = gnupg_create_inbound_pipe (inpipe, &infp, 0);
|
||||||
|
if (err)
|
||||||
|
{
|
||||||
|
log_error ("error creating inbound pipe: %s\n", gpg_strerror (err));
|
||||||
|
return err; /* That should not happen. */
|
||||||
|
}
|
||||||
|
|
||||||
|
err = assuan_sendfd (kcd->ctx, INT2FD (inpipe[1]));
|
||||||
|
if (err)
|
||||||
|
{
|
||||||
|
log_error ("sending sending fd %d to keyboxd: %s <%s>\n",
|
||||||
|
inpipe[1], gpg_strerror (err), gpg_strsource (err));
|
||||||
|
es_fclose (infp);
|
||||||
|
gnupg_close_pipe (inpipe[1]);
|
||||||
|
return 0; /* Server may not support fd-passing. */
|
||||||
|
}
|
||||||
|
|
||||||
|
err = assuan_transact (kcd->ctx, "OUTPUT FD",
|
||||||
|
NULL, NULL, NULL, NULL, NULL, NULL);
|
||||||
|
if (err)
|
||||||
|
{
|
||||||
|
log_info ("keyboxd does not accept our fd: %s <%s>\n",
|
||||||
|
gpg_strerror (err), gpg_strsource (err));
|
||||||
|
es_fclose (infp);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
kcd->fp = infp;
|
||||||
|
|
||||||
|
|
||||||
|
rc = npth_attr_init (&tattr);
|
||||||
|
if (rc)
|
||||||
|
{
|
||||||
|
err = gpg_error_from_errno (rc);
|
||||||
|
log_error ("error preparing thread for keyboxd: %s\n",gpg_strerror (err));
|
||||||
|
es_fclose (infp);
|
||||||
|
kcd->fp = NULL;
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
npth_attr_setdetachstate (&tattr, NPTH_CREATE_DETACHED);
|
||||||
|
rc = npth_create (&thread, &tattr, datastream_thread, kcd);
|
||||||
|
if (rc)
|
||||||
|
{
|
||||||
|
err = gpg_error_from_errno (rc);
|
||||||
|
log_error ("error spawning thread for keyboxd: %s\n", gpg_strerror (err));
|
||||||
|
npth_attr_destroy (&tattr);
|
||||||
|
es_fclose (infp);
|
||||||
|
kcd->fp = NULL;
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* The thread used to read from the data stream. This is running as
|
||||||
|
* long as the connection and its datastream exists. */
|
||||||
|
static void *
|
||||||
|
datastream_thread (void *arg)
|
||||||
|
{
|
||||||
|
kbx_client_data_t kcd = arg;
|
||||||
|
gpg_error_t err;
|
||||||
|
int rc;
|
||||||
|
unsigned char lenbuf[4];
|
||||||
|
size_t nread, datalen;
|
||||||
|
int pk_no, uid_no;
|
||||||
|
char *data, *tmpdata;
|
||||||
|
|
||||||
|
/* log_debug ("%s: started\n", __func__); */
|
||||||
|
while (kcd->fp)
|
||||||
|
{
|
||||||
|
/* log_debug ("%s: waiting ...\n", __func__); */
|
||||||
|
if (es_read (kcd->fp, lenbuf, 4, &nread))
|
||||||
|
{
|
||||||
|
err = gpg_error_from_syserror ();
|
||||||
|
if (gpg_err_code (err) == GPG_ERR_EAGAIN)
|
||||||
|
continue;
|
||||||
|
log_error ("error reading data length from keyboxd: %s\n",
|
||||||
|
gpg_strerror (err));
|
||||||
|
gnupg_sleep (1);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (nread != 4)
|
||||||
|
{
|
||||||
|
err = gpg_error (GPG_ERR_EIO);
|
||||||
|
log_error ("error reading data length from keyboxd: %s\n",
|
||||||
|
"short read");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
datalen = buf32_to_size_t (lenbuf);
|
||||||
|
/* log_debug ("keyboxd announced %zu bytes\n", datalen); */
|
||||||
|
if (!datalen)
|
||||||
|
{
|
||||||
|
log_info ("ignoring empty blob received from keyboxd\n");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (datalen > MAX_DATABLOB_SIZE)
|
||||||
|
{
|
||||||
|
err = gpg_error (GPG_ERR_TOO_LARGE);
|
||||||
|
/* Drop connection or what shall we do? */
|
||||||
|
}
|
||||||
|
else if (!(data = xtrymalloc (datalen+1)))
|
||||||
|
{
|
||||||
|
err = gpg_error_from_syserror ();
|
||||||
|
}
|
||||||
|
else if (es_read (kcd->fp, data, datalen, &nread))
|
||||||
|
{
|
||||||
|
err = gpg_error_from_syserror ();
|
||||||
|
}
|
||||||
|
else if (datalen != nread)
|
||||||
|
{
|
||||||
|
err = gpg_error (GPG_ERR_TOO_SHORT);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
err = 0;
|
||||||
|
|
||||||
|
if (err)
|
||||||
|
{
|
||||||
|
log_error ("error reading data from keyboxd: %s <%s>\n",
|
||||||
|
gpg_strerror (err), gpg_strsource (err));
|
||||||
|
xfree (data);
|
||||||
|
data = NULL;
|
||||||
|
datalen = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* log_debug ("parsing datastream succeeded\n"); */
|
||||||
|
pk_no = uid_no = 0; /* FIXME: Get this from the keyboxd. */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Thread-safe assignment to the result var: */
|
||||||
|
tmpdata = kcd->data;
|
||||||
|
kcd->data = data;
|
||||||
|
kcd->datalen = datalen;
|
||||||
|
kcd->dataerr = err;
|
||||||
|
xfree (tmpdata);
|
||||||
|
data = NULL;
|
||||||
|
|
||||||
|
/* Tell the main thread. */
|
||||||
|
lock_datastream (kcd);
|
||||||
|
rc = npth_cond_signal (&kcd->cond);
|
||||||
|
if (rc)
|
||||||
|
{
|
||||||
|
err = gpg_error_from_errno (rc);
|
||||||
|
log_error ("%s: signaling condition failed: %s\n",
|
||||||
|
__func__, gpg_strerror (err));
|
||||||
|
}
|
||||||
|
unlock_datastream (kcd);
|
||||||
|
}
|
||||||
|
/* log_debug ("%s: finished\n", __func__); */
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Create a new keyboxd client data object and return it at R_KCD.
|
||||||
|
* CTX is the assuan context to be used for connecting the
|
||||||
|
* keyboxd. */
|
||||||
|
gpg_error_t
|
||||||
|
kbx_client_data_new (kbx_client_data_t *r_kcd, assuan_context_t ctx)
|
||||||
|
{
|
||||||
|
kbx_client_data_t kcd;
|
||||||
|
int rc;
|
||||||
|
gpg_error_t err;
|
||||||
|
|
||||||
|
kcd = xtrycalloc (1, sizeof *kcd);
|
||||||
|
if (!kcd)
|
||||||
|
return gpg_error_from_syserror ();
|
||||||
|
|
||||||
|
kcd->ctx = ctx;
|
||||||
|
|
||||||
|
rc = npth_mutex_init (&kcd->mutex, NULL);
|
||||||
|
if (rc)
|
||||||
|
{
|
||||||
|
err = gpg_error_from_errno (rc);
|
||||||
|
log_error ("error initializing mutex: %s\n", gpg_strerror (err));
|
||||||
|
xfree (kcd);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
rc = npth_cond_init (&kcd->cond, NULL);
|
||||||
|
if (rc)
|
||||||
|
{
|
||||||
|
err = gpg_error_from_errno (rc);
|
||||||
|
log_error ("error initializing condition: %s\n", gpg_strerror (err));
|
||||||
|
npth_mutex_destroy (&kcd->mutex);
|
||||||
|
xfree (kcd);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = prepare_data_pipe (kcd);
|
||||||
|
if (err)
|
||||||
|
{
|
||||||
|
npth_cond_destroy (&kcd->cond);
|
||||||
|
npth_mutex_destroy (&kcd->mutex);
|
||||||
|
xfree (kcd);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
*r_kcd = kcd;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
kbx_client_data_release (kbx_client_data_t kcd)
|
||||||
|
{
|
||||||
|
estream_t fp;
|
||||||
|
|
||||||
|
if (!kcd)
|
||||||
|
return;
|
||||||
|
fp = kcd->fp;
|
||||||
|
kcd->fp = NULL;
|
||||||
|
es_fclose (fp); /* That close should let the thread run into an error. */
|
||||||
|
/* FIXME: Make thread killing explicit. Otherwise we run in a
|
||||||
|
* log_fatal due to the destroyed mutex. */
|
||||||
|
npth_cond_destroy (&kcd->cond);
|
||||||
|
npth_mutex_destroy (&kcd->mutex);
|
||||||
|
xfree (kcd);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Send the COMMAND down to the keyboxd associated with KCD.
|
||||||
|
* STATUS_CB and STATUS_CB_VALUE are the usual status callback as used
|
||||||
|
* by assuan_transact. After this function has returned success
|
||||||
|
* kbx_client_data_wait needs to be called to actually return the
|
||||||
|
* data. */
|
||||||
|
gpg_error_t
|
||||||
|
kbx_client_data_cmd (kbx_client_data_t kcd, const char *command,
|
||||||
|
gpg_error_t (*status_cb)(void *opaque, const char *line),
|
||||||
|
void *status_cb_value)
|
||||||
|
{
|
||||||
|
gpg_error_t err;
|
||||||
|
|
||||||
|
xfree (kcd->dlinedata);
|
||||||
|
kcd->dlinedata = NULL;
|
||||||
|
kcd->dlinedatalen = 0;
|
||||||
|
kcd->dlineerr = 0;
|
||||||
|
|
||||||
|
if (kcd->fp)
|
||||||
|
{
|
||||||
|
/* log_debug ("%s: sending command '%s'\n", __func__, command); */
|
||||||
|
err = assuan_transact (kcd->ctx, command,
|
||||||
|
NULL, NULL,
|
||||||
|
NULL, NULL,
|
||||||
|
status_cb, status_cb_value);
|
||||||
|
if (err)
|
||||||
|
{
|
||||||
|
if (gpg_err_code (err) != GPG_ERR_NOT_FOUND)
|
||||||
|
log_debug ("%s: finished command with error: %s\n",
|
||||||
|
__func__, gpg_strerror (err));
|
||||||
|
/* Fixme: On unexpected errors we need a way to cancel the
|
||||||
|
* data stream. Probably it will be best to close and
|
||||||
|
* reopen it. */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else /* Slower D-line version if fd-passing is not available. */
|
||||||
|
{
|
||||||
|
membuf_t mb;
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
/* log_debug ("%s: sending command '%s' (no fd-passing)\n", */
|
||||||
|
/* __func__, command); */
|
||||||
|
init_membuf (&mb, 8192);
|
||||||
|
err = assuan_transact (kcd->ctx, command,
|
||||||
|
put_membuf_cb, &mb,
|
||||||
|
NULL, NULL,
|
||||||
|
status_cb, status_cb_value);
|
||||||
|
if (err)
|
||||||
|
{
|
||||||
|
if (gpg_err_code (err) != GPG_ERR_NOT_FOUND)
|
||||||
|
log_debug ("%s: finished command with error: %s\n",
|
||||||
|
__func__, gpg_strerror (err));
|
||||||
|
xfree (get_membuf (&mb, &len));
|
||||||
|
kcd->dlineerr = err;
|
||||||
|
goto leave;
|
||||||
|
}
|
||||||
|
|
||||||
|
kcd->dlinedata = get_membuf (&mb, &kcd->dlinedatalen);
|
||||||
|
if (!kcd->dlinedata)
|
||||||
|
{
|
||||||
|
err = gpg_error_from_syserror ();
|
||||||
|
goto leave;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
leave:
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Wait for the data from the server and on success return it at
|
||||||
|
* (R_DATA, R_DATALEN). */
|
||||||
|
gpg_error_t
|
||||||
|
kbx_client_data_wait (kbx_client_data_t kcd, char **r_data, size_t *r_datalen)
|
||||||
|
{
|
||||||
|
gpg_error_t err = 0;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
*r_data = NULL;
|
||||||
|
*r_datalen = 0;
|
||||||
|
if (kcd->fp)
|
||||||
|
{
|
||||||
|
lock_datastream (kcd);
|
||||||
|
if (!kcd->data && !kcd->dataerr)
|
||||||
|
{
|
||||||
|
/* log_debug ("%s: waiting on datastream_cond ...\n", __func__); */
|
||||||
|
rc = npth_cond_wait (&kcd->cond, &kcd->mutex);
|
||||||
|
if (rc)
|
||||||
|
{
|
||||||
|
err = gpg_error_from_errno (rc);
|
||||||
|
log_error ("%s: waiting on condition failed: %s\n",
|
||||||
|
__func__, gpg_strerror (err));
|
||||||
|
}
|
||||||
|
/* else */
|
||||||
|
/* log_debug ("%s: waiting on datastream.cond done\n", __func__); */
|
||||||
|
}
|
||||||
|
*r_data = kcd->data;
|
||||||
|
kcd->data = NULL;
|
||||||
|
*r_datalen = kcd->datalen;
|
||||||
|
err = err? err : kcd->dataerr;
|
||||||
|
|
||||||
|
unlock_datastream (kcd);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
*r_data = kcd->dlinedata;
|
||||||
|
kcd->dlinedata = NULL;
|
||||||
|
*r_datalen = kcd->dlinedatalen;
|
||||||
|
err = kcd->dlineerr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
41
kbx/kbx-client-util.h
Normal file
41
kbx/kbx-client-util.h
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
/* kbx-client-util.c - Defs for utility functions for a keyboxd client
|
||||||
|
* Copyright (C) 2020 g10 Code GmbH
|
||||||
|
*
|
||||||
|
* This file is part of GnuPG.
|
||||||
|
*
|
||||||
|
* GnuPG 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 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* GnuPG 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.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, see <https://www.gnu.org/licenses/>.
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef GNUPG_KBX_CLIENT_UTIL_H
|
||||||
|
#define GNUPG_KBX_CLIENT_UTIL_H 1
|
||||||
|
|
||||||
|
|
||||||
|
struct kbx_client_data_s;
|
||||||
|
typedef struct kbx_client_data_s *kbx_client_data_t;
|
||||||
|
|
||||||
|
gpg_error_t kbx_client_data_new (kbx_client_data_t *r_kcd,
|
||||||
|
assuan_context_t ctx);
|
||||||
|
void kbx_client_data_release (kbx_client_data_t kcd);
|
||||||
|
gpg_error_t kbx_client_data_cmd (kbx_client_data_t kcd, const char *command,
|
||||||
|
gpg_error_t (*status_cb)(void *opaque,
|
||||||
|
const char *line),
|
||||||
|
void *status_cb_value);
|
||||||
|
gpg_error_t kbx_client_data_wait (kbx_client_data_t kcd,
|
||||||
|
char **r_data, size_t *r_datalen);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif /*GNUPG_KBX_CLIENT_UTIL_H*/
|
Loading…
x
Reference in New Issue
Block a user