aboutsummaryrefslogtreecommitdiffstats
path: root/g10/call-keyboxd.c
diff options
context:
space:
mode:
Diffstat (limited to 'g10/call-keyboxd.c')
-rw-r--r--g10/call-keyboxd.c420
1 files changed, 341 insertions, 79 deletions
diff --git a/g10/call-keyboxd.c b/g10/call-keyboxd.c
index f05a9e85f..97f84c03d 100644
--- a/g10/call-keyboxd.c
+++ b/g10/call-keyboxd.c
@@ -28,6 +28,7 @@
#ifdef HAVE_LOCALE_H
# include <locale.h>
#endif
+#include <npth.h>
#include "gpg.h"
#include <assuan.h>
@@ -36,6 +37,8 @@
#include "options.h"
#include "../common/i18n.h"
#include "../common/asshelp.h"
+#include "../common/host2net.h"
+#include "../common/exechelp.h"
#include "../common/status.h"
#include "keydb.h"
@@ -54,17 +57,63 @@ struct keyboxd_local_s
/* The active Assuan context. */
assuan_context_t ctx;
+ /* This object is used if fd-passing is used to convey the
+ * keyblocks. */
+ 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
+ * D-lines are used to convey the keyblocks. */
+ iobuf_t search_result;
+
/* This flag set while an operation is running on this context. */
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. */
+ 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. */
void
gpg_keyboxd_deinit_session_data (ctrl_t ctrl)
@@ -77,7 +126,12 @@ gpg_keyboxd_deinit_session_data (ctrl_t ctrl)
if (kbl->is_active)
log_error ("oops: trying to cleanup an active keyboxd context\n");
else
- assuan_release (kbl->ctx);
+ {
+ es_fclose (kbl->datastream.fp);
+ kbl->datastream.fp = NULL;
+ assuan_release (kbl->ctx);
+ kbl->ctx = NULL;
+ }
xfree (kbl);
}
}
@@ -165,18 +219,86 @@ 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
- * new one is created and if necessary keyboxd is started. On success
- * an assuan context is stored at R_CTX. This context may only be
- * released by means of close_context. Note that NULL is stored at
- * R_CTX on error. */
+ * new one is created and if necessary keyboxd is started. R_KBL
+ * receives a pointer to the local context object. */
static gpg_error_t
-open_context (ctrl_t ctrl, assuan_context_t *r_ctx)
+open_context (ctrl_t ctrl, keyboxd_local_t *r_kbl)
{
gpg_error_t err;
+ int rc;
keyboxd_local_t kbl;
- *r_ctx = NULL;
+ *r_kbl = NULL;
for (;;)
{
for (kbl = ctrl->keyboxd_local; kbl && kbl->is_active; kbl = kbl->next)
@@ -189,12 +311,16 @@ open_context (ctrl_t ctrl, assuan_context_t *r_ctx)
/* 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->need_search_reset = 1;
- *r_ctx = kbl->ctx;
+ *r_kbl = kbl;
return 0;
}
@@ -202,43 +328,40 @@ open_context (ctrl_t ctrl, assuan_context_t *r_ctx)
kbl = xtrycalloc (1, sizeof *kbl);
if (!kbl)
return gpg_error_from_syserror ();
+
+ rc = npth_mutex_init (&kbl->datastream.mutex, NULL);
+ if (rc)
+ {
+ 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);
+ return err;
+ }
+
err = create_new_context (ctrl, &kbl->ctx);
if (err)
{
+ npth_cond_destroy (&kbl->datastream.cond);
+ npth_mutex_destroy (&kbl->datastream.mutex);
xfree (kbl);
return err;
}
- /* Although we are not yet using nPth in gpg we better prepare
- * to be nPth thread safe. Thus we add it to the list and
- * retry; this is easier than to employ a lock. */
+ /* For thread-saftey we add it to the list and retry; this is
+ * easier than to employ a lock. */
kbl->next = ctrl->keyboxd_local;
ctrl->keyboxd_local = kbl;
}
-}
-
-
-/* Close the assuan context CTX and return it to a pool of unused
- * contexts. If CTX is NULL, the function does nothing. */
-static void
-close_context (ctrl_t ctrl, assuan_context_t ctx)
-{
- keyboxd_local_t kbl;
-
- if (!ctx)
- return;
-
- for (kbl = ctrl->keyboxd_local; kbl; kbl = kbl->next)
- {
- if (kbl->ctx == ctx)
- {
- if (!kbl->is_active)
- log_fatal ("closing inactive keyboxd context %p\n", ctx);
- kbl->is_active = 0;
- return;
- }
- }
- log_fatal ("closing unknown keyboxd ctx %p\n", ctx);
+ /*NOTREACHED*/
}
@@ -275,11 +398,8 @@ keydb_new (ctrl_t ctrl)
}
hd->use_keyboxd = 1;
hd->ctrl = ctrl;
- hd->need_search_reset = 1;
- err = open_context (ctrl, &hd->assuan_context);
- if (err)
- goto leave;
+ err = open_context (ctrl, &hd->kbl);
leave:
if (err)
@@ -300,14 +420,25 @@ keydb_new (ctrl_t ctrl)
void
keydb_release (KEYDB_HANDLE hd)
{
+ keyboxd_local_t kbl;
+
if (!hd)
return;
+ if (DBG_CLOCK)
+ log_clock ("keydb_release");
if (!hd->use_keyboxd)
internal_keydb_deinit (hd);
else
{
- close_context (hd->ctrl, hd->assuan_context);
+ kbl = hd->kbl;
+ if (DBG_CLOCK)
+ log_clock ("close_context (found)");
+ if (!kbl->is_active)
+ log_fatal ("closing inactive keyboxd context %p\n", kbl);
+ kbl->is_active = 0;
+ hd->kbl = NULL;
+ hd->ctrl = NULL;
}
xfree (hd);
}
@@ -465,6 +596,86 @@ keydb_get_keyblock_do_parse (iobuf_t iobuf, int pk_no, int uid_no,
}
+/* 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.
*
* On success, the function returns 0 and the caller must free *RET_KB
@@ -494,20 +705,28 @@ keydb_get_keyblock (KEYDB_HANDLE hd, kbnode_t *ret_kb)
goto leave;
}
- if (!hd->search_result)
+ if (hd->kbl->search_result)
+ {
+ pk_no = uid_no = 0; /*FIXME: Get this from the keyboxd. */
+ err = keydb_get_keyblock_do_parse (hd->kbl->search_result,
+ pk_no, uid_no, ret_kb);
+ /* In contrast to the old code we close the iobuf here and thus
+ * this function may be called only once to get a keyblock. */
+ iobuf_close (hd->kbl->search_result);
+ 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
{
err = gpg_error (GPG_ERR_VALUE_NOT_FOUND);
goto leave;
}
- pk_no = uid_no = 0; /*FIXME: Get this from the keyboxd. */
- err = keydb_get_keyblock_do_parse (hd->search_result, pk_no, uid_no, ret_kb);
- /* In contrast to the old code we close the iobuf here and thus this
- * function may be called only once to get a keyblock. */
- iobuf_close (hd->search_result);
- hd->search_result = NULL;
-
-
leave:
if (DBG_CLOCK)
log_clock ("%s leave%s", __func__, err? " (failed)":"");
@@ -636,7 +855,7 @@ keydb_search_reset (KEYDB_HANDLE hd)
/* All we need todo is to tell search that a reset is pending. Noet
* that keydb_new sets this flag as well. */
- hd->need_search_reset = 1;
+ hd->kbl->need_search_reset = 1;
err = 0;
leave:
@@ -697,14 +916,20 @@ keydb_search (KEYDB_HANDLE hd, KEYDB_SEARCH_DESC *desc,
goto leave;
}
-
- if (hd->search_result)
+ /* Clear the result objects. */
+ if (hd->kbl->search_result)
{
- iobuf_close (hd->search_result);
- hd->search_result = NULL;
+ iobuf_close (hd->kbl->search_result);
+ hd->kbl->search_result = NULL;
+ }
+ if (hd->kbl->datastream.found_keyblock)
+ {
+ release_kbnode (hd->kbl->datastream.found_keyblock);
+ hd->kbl->datastream.found_keyblock = NULL;
}
- if (!hd->need_search_reset)
+ /* Check whether this is a NEXT search. */
+ if (!hd->kbl->need_search_reset)
{
/* No reset requested thus continue the search. The keyboxd
* keeps the context of the search and thus the NEXT operates on
@@ -717,7 +942,7 @@ keydb_search (KEYDB_HANDLE hd, KEYDB_SEARCH_DESC *desc,
goto do_search;
}
- hd->need_search_reset = 0;
+ hd->kbl->need_search_reset = 0;
if (!ndesc)
{
@@ -807,31 +1032,68 @@ keydb_search (KEYDB_HANDLE hd, KEYDB_SEARCH_DESC *desc,
}
do_search:
- {
- membuf_t data;
- void *buffer;
- size_t len;
-
- init_membuf (&data, 8192);
- err = assuan_transact (hd->assuan_context, line,
- put_membuf_cb, &data,
- NULL, NULL,
- NULL, NULL);
- if (err)
- {
- xfree (get_membuf (&data, &len));
- goto leave;
- }
+ if (hd->kbl->datastream.fp)
+ {
+ /* log_debug ("Sending command '%s'\n", line); */
+ err = assuan_transact (hd->kbl->ctx, line,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL);
+ if (err)
+ {
+ /* log_debug ("Finished command with error: %s\n", gpg_strerror (err)); */
+ /* Fixme: On unexpected errors we need a way to cancek the
+ * data stream. Probly it will be best to closeand reopen
+ * it. */
+ }
+ else
+ {
+ int rc;
- buffer = get_membuf (&data, &len);
- if (!buffer)
- {
- err = gpg_error_from_syserror ();
- goto leave;
- }
+ /* 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,
+ NULL, NULL);
+ if (err)
+ {
+ xfree (get_membuf (&data, &len));
+ goto leave;
+ }
+
+ buffer = get_membuf (&data, &len);
+ if (!buffer)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
- /* Fixme: Avoid double allocation. */
- hd->search_result = iobuf_temp_with_content (buffer, len);
+ hd->kbl->search_result = iobuf_temp_with_content (buffer, len);
+ xfree (buffer);
}