aboutsummaryrefslogtreecommitdiffstats
path: root/g10
diff options
context:
space:
mode:
Diffstat (limited to 'g10')
-rw-r--r--g10/ChangeLog24
-rw-r--r--g10/Makefile.am1
-rw-r--r--g10/call-dirmngr.c611
-rw-r--r--g10/call-dirmngr.h32
-rw-r--r--g10/export.c54
-rw-r--r--g10/gpg.c45
-rw-r--r--g10/gpg.h8
-rw-r--r--g10/import.c26
-rw-r--r--g10/keyserver-internal.h2
-rw-r--r--g10/keyserver.c1438
-rw-r--r--g10/main.h7
-rw-r--r--g10/options.h28
12 files changed, 1352 insertions, 924 deletions
diff --git a/g10/ChangeLog b/g10/ChangeLog
index 8e79587d8..4c28363a2 100644
--- a/g10/ChangeLog
+++ b/g10/ChangeLog
@@ -1,3 +1,25 @@
+2011-01-20 Werner Koch <[email protected]>
+
+ * keyserver.c: Rewrite most stuff for use with dirmngr. Get rid
+ of all spawn code. Work work pending.
+
+ * export.c (export_pubkeys_buffer): New.
+
+ * import.c (import_keys_es_stream): New.
+
+ * call-dirmngr.c, call-dirmngr.h: New.
+ * gpg.h (server_control_s): Add DIRMNGR_LOCAL.
+ * gpg.c: Include call-dirmngr.h.
+ (gpg_deinit_default_ctrl): Call gpg_dirmngr_deinit_session_data.
+
+2011-01-06 Werner Koch <[email protected]>
+
+ * gpg.c (main): Use keyserver_spec_t.
+
+ * options.h (struct opt): Factor definition of struct keyserver
+ out to ../common/keyserver.h.
+ (keyserver_spec_t): New.
+
2011-01-21 Werner Koch <[email protected]>
* seskey.c (encode_md_value): Truncate the DSA hash again.
@@ -11654,7 +11676,7 @@ Thu Feb 12 22:24:42 1998 Werner Koch (wk@frodo)
Copyright 1998,1999,2000,2001,2002,2003,2004,2005,
- 2006,2007,2008,2009,2010 Free Software Foundation, Inc.
+ 2006,2007,2008,2009,2010,2011 Free Software Foundation, Inc.
This file is free software; as a special exception the author gives
unlimited permission to copy and/or distribute it, with or without
diff --git a/g10/Makefile.am b/g10/Makefile.am
index b82fe07f3..5d2470207 100644
--- a/g10/Makefile.am
+++ b/g10/Makefile.am
@@ -103,6 +103,7 @@ gpg2_SOURCES = gpg.c \
helptext.c \
keyserver.c \
keyserver-internal.h \
+ call-dirmngr.c call-dirmngr.h \
photoid.c photoid.h \
call-agent.c call-agent.h \
card-util.c \
diff --git a/g10/call-dirmngr.c b/g10/call-dirmngr.c
new file mode 100644
index 000000000..f34b94b60
--- /dev/null
+++ b/g10/call-dirmngr.c
@@ -0,0 +1,611 @@
+/* call-dirmngr.c - GPG operations to the Dirmngr.
+ * Copyright (C) 2011 Free Software Foundation, Inc.
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <time.h>
+#include <assert.h>
+#ifdef HAVE_LOCALE_H
+# include <locale.h>
+#endif
+
+#include "gpg.h"
+#include <assuan.h>
+#include "util.h"
+#include "membuf.h"
+#include "options.h"
+#include "i18n.h"
+#include "asshelp.h"
+#include "keyserver.h"
+#include "call-dirmngr.h"
+
+
+/* Parameter structure used with the KS_SEARCH command. */
+struct ks_search_parm_s
+{
+ gpg_error_t lasterr; /* Last error code. */
+ membuf_t saveddata; /* Buffer to build complete lines. */
+ char *helpbuf; /* NULL or malloced buffer. */
+ size_t helpbufsize; /* Allocated size of HELPBUF. */
+ gpg_error_t (*data_cb)(void*, char*); /* Callback. */
+ void *data_cb_value; /* First argument for DATA_CB. */
+};
+
+
+/* Parameter structure used with the KS_GET command. */
+struct ks_get_parm_s
+{
+ estream_t memfp;
+};
+
+
+/* Parameter structure used with the KS_PUT command. */
+struct ks_put_parm_s
+{
+ assuan_context_t ctx;
+ kbnode_t keyblock; /* The optional keyblock. */
+ const void *data; /* The key in OpenPGP binary format. */
+ size_t datalen; /* The length of DATA. */
+};
+
+
+/* Data used to associate an session with dirmngr contexts. We can't
+ use a simple one to one mapping because we sometimes need two
+ connection s to the dirmngr; for example while doing a listing and
+ being in a data callback we may want to retrieve a key. The local
+ dirmngr data takes care of this. At the end of the session the
+ function dirmngr_deinit_session_data is called bu gpg.c to cleanup
+ these resources. Note that gpg.h defines a typedef dirmngr_local_t
+ for this structure. */
+struct dirmngr_local_s
+{
+ /* Link to other contexts which are used simultaneously. */
+ struct dirmngr_local_s *next;
+
+ /* The active Assuan context. */
+ assuan_context_t ctx;
+
+ /* Flag set to true while an operation is running on CTX. */
+ int is_active;
+};
+
+
+
+/* Deinitialize all session data of dirmngr pertaining to CTRL. */
+void
+gpg_dirmngr_deinit_session_data (ctrl_t ctrl)
+{
+ dirmngr_local_t dml;
+
+ while ((dml = ctrl->dirmngr_local))
+ {
+ ctrl->dirmngr_local = dml->next;
+ if (dml->is_active)
+ log_error ("oops: trying to cleanup an active dirmngr context\n");
+ else
+ assuan_release (dml->ctx);
+ xfree (dml);
+ }
+}
+
+
+/* Try to connect to the Dirmngr via a socket or fork it off if
+ possible. Handle the server's initial greeting and set global
+ options. */
+static gpg_error_t
+create_context (ctrl_t ctrl, assuan_context_t *r_ctx)
+{
+ gpg_error_t err;
+ assuan_context_t ctx;
+
+ *r_ctx = NULL;
+ err = start_new_dirmngr (&ctx,
+ GPG_ERR_SOURCE_DEFAULT,
+ opt.homedir,
+ NULL,
+ opt.verbose, DBG_ASSUAN,
+ NULL /*gpg_status2*/, ctrl);
+ if (!err)
+ {
+ keyserver_spec_t ksi;
+
+ /* Tell the dirmngr that we want to collect audit event. */
+ /* err = assuan_transact (agent_ctx, "OPTION audit-events=1", */
+ /* NULL, NULL, NULL, NULL, NULL, NULL); */
+
+ /* Set all configured keyservers. We clear existing keyservers
+ so that any keyserver configured in GPG overrides keyservers
+ possibly configured in Dirmngr. */
+ for (ksi = opt.keyserver; !err && ksi; ksi = ksi->next)
+ {
+ char *line;
+
+ line = xtryasprintf ("KEYSERVER%s %s",
+ ksi == opt.keyserver? " --clear":"", ksi->uri);
+ if (!line)
+ err = gpg_error_from_syserror ();
+ else
+ {
+ err = assuan_transact (ctx, line,
+ NULL, NULL, NULL, NULL, NULL, NULL);
+ xfree (line);
+ }
+ }
+ }
+
+ if (err)
+ assuan_release (ctx);
+ else
+ {
+ /* audit_log_ok (ctrl->audit, AUDIT_DIRMNGR_READY, err); */
+ *r_ctx = ctx;
+ }
+
+ return err;
+}
+
+
+/* Get a context for accessing dirmngr. If no context is available a
+ new one is created and - if requred - dirmngr 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. */
+static gpg_error_t
+open_context (ctrl_t ctrl, assuan_context_t *r_ctx)
+{
+ gpg_error_t err;
+ dirmngr_local_t dml;
+
+ *r_ctx = NULL;
+ for (;;)
+ {
+ for (dml = ctrl->dirmngr_local; dml && dml->is_active; dml = dml->next)
+ ;
+ if (dml)
+ {
+ /* Found an inactive local session - return that. */
+ assert (!dml->is_active);
+ dml->is_active = 1;
+ *r_ctx = dml->ctx;
+ return 0;
+ }
+
+ dml = xtrycalloc (1, sizeof *dml);
+ if (!dml)
+ return gpg_error_from_syserror ();
+ err = create_context (ctrl, &dml->ctx);
+ if (err)
+ {
+ xfree (dml);
+ return err;
+ }
+ /* To be on the Pth thread safe site we need to add it to a
+ list; this is far easier than to have a lock for this
+ function. It should not happen anyway but the code is free
+ because we need it for the is_active check above. */
+ dml->next = ctrl->dirmngr_local;
+ ctrl->dirmngr_local = dml;
+ }
+}
+
+
+/* Close the assuan context CTX or 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)
+{
+ dirmngr_local_t dml;
+
+ if (!ctx)
+ return;
+
+ for (dml = ctrl->dirmngr_local; dml; dml = dml->next)
+ {
+ if (dml->ctx == ctx)
+ {
+ if (!dml->is_active)
+ log_fatal ("closing inactive dirmngr context %p\n", ctx);
+ dml->is_active = 0;
+ return;
+ }
+ }
+ log_fatal ("closing unknown dirmngr ctx %p\n", ctx);
+}
+
+
+
+/* Data callback for the KS_SEARCH command. */
+static gpg_error_t
+ks_search_data_cb (void *opaque, const void *data, size_t datalen)
+{
+ gpg_error_t err = 0;
+ struct ks_search_parm_s *parm = opaque;
+ const char *line, *s;
+ size_t rawlen, linelen;
+ char fixedbuf[256];
+
+ if (parm->lasterr)
+ return 0;
+
+ if (!data)
+ return 0; /* Ignore END commands. */
+
+ put_membuf (&parm->saveddata, data, datalen);
+
+ again:
+ line = peek_membuf (&parm->saveddata, &rawlen);
+ if (!line)
+ {
+ parm->lasterr = gpg_error_from_syserror ();
+ return parm->lasterr; /* Tell the server about our problem. */
+ }
+ if ((s = memchr (line, '\n', rawlen)))
+ {
+ linelen = s - line; /* That is the length excluding the LF. */
+ if (linelen + 1 < sizeof fixedbuf)
+ {
+ /* We can use the static buffer. */
+ memcpy (fixedbuf, line, linelen);
+ fixedbuf[linelen] = 0;
+ if (linelen && fixedbuf[linelen-1] == '\r')
+ fixedbuf[linelen-1] = 0;
+ err = parm->data_cb (parm->data_cb_value, fixedbuf);
+ }
+ else
+ {
+ if (linelen + 1 >= parm->helpbufsize)
+ {
+ xfree (parm->helpbuf);
+ parm->helpbufsize = linelen + 1 + 1024;
+ parm->helpbuf = xtrymalloc (parm->helpbufsize);
+ if (!parm->helpbuf)
+ {
+ parm->lasterr = gpg_error_from_syserror ();
+ return parm->lasterr;
+ }
+ }
+ memcpy (parm->helpbuf, line, linelen);
+ parm->helpbuf[linelen] = 0;
+ if (linelen && parm->helpbuf[linelen-1] == '\r')
+ parm->helpbuf[linelen-1] = 0;
+ err = parm->data_cb (parm->data_cb_value, parm->helpbuf);
+ }
+ if (err)
+ parm->lasterr = err;
+ else
+ {
+ clear_membuf (&parm->saveddata, linelen+1);
+ goto again; /* There might be another complete line. */
+ }
+ }
+
+ return err;
+}
+
+
+/* Run the KS_SEARCH command using the search string SEARCHSTR. All
+ data lines are passed to the CB function. That function is called
+ with CB_VALUE as its first argument and the decoded data line as
+ second argument. The callback function may modify the data line
+ and it is guaranteed that this data line is a complete line with a
+ terminating 0 character but without the linefeed. NULL is passed
+ to the callback to indicate EOF. */
+gpg_error_t
+gpg_dirmngr_ks_search (ctrl_t ctrl, const char *searchstr,
+ gpg_error_t (*cb)(void*, char *), void *cb_value)
+{
+ gpg_error_t err;
+ assuan_context_t ctx;
+ struct ks_search_parm_s parm;
+ char line[ASSUAN_LINELENGTH];
+
+ err = open_context (ctrl, &ctx);
+ if (err)
+ return err;
+
+ {
+ char *escsearchstr = percent_plus_escape (searchstr);
+ if (!escsearchstr)
+ {
+ err = gpg_error_from_syserror ();
+ close_context (ctrl, ctx);
+ return err;
+ }
+ snprintf (line, sizeof line, "KS_SEARCH -- %s", escsearchstr);
+ xfree (escsearchstr);
+ }
+
+ memset (&parm, 0, sizeof parm);
+ init_membuf (&parm.saveddata, 1024);
+ parm.data_cb = cb;
+ parm.data_cb_value = cb_value;
+
+ err = assuan_transact (ctx, line, ks_search_data_cb, &parm,
+ NULL, NULL, NULL, NULL);
+ if (!err)
+ err = cb (cb_value, NULL); /* Send EOF. */
+
+ xfree (get_membuf (&parm.saveddata, NULL));
+ xfree (parm.helpbuf);
+
+ close_context (ctrl, ctx);
+ return err;
+}
+
+
+
+/* Data callback for the KS_GET command. */
+static gpg_error_t
+ks_get_data_cb (void *opaque, const void *data, size_t datalen)
+{
+ gpg_error_t err = 0;
+ struct ks_get_parm_s *parm = opaque;
+ size_t nwritten;
+
+ if (!data)
+ return 0; /* Ignore END commands. */
+
+ if (es_write (parm->memfp, data, datalen, &nwritten))
+ err = gpg_error_from_syserror ();
+
+ return err;
+}
+
+
+/* Run the KS_GET command using the patterns in the array PATTERN. On
+ success an estream object is returned to retrieve the keys. On
+ error an error code is returned and NULL stored at R_FP.
+
+ The pattern may only use search specification which a keyserver can
+ use to retriev keys. Because we know the format of the pattern we
+ don't need to escape the patterns before sending them to the
+ server.
+
+ If there are too many patterns the function returns an error. That
+ could be fixed by issuing several search commands or by
+ implementing a different interface. However with long keyids we
+ are able to ask for (1000-10-1)/(2+8+1) = 90 keys at once. */
+gpg_error_t
+gpg_dirmngr_ks_get (ctrl_t ctrl, char **pattern, estream_t *r_fp)
+{
+ gpg_error_t err;
+ assuan_context_t ctx;
+ struct ks_get_parm_s parm;
+ char *line = NULL;
+ size_t linelen;
+ membuf_t mb;
+ int idx;
+
+ memset (&parm, 0, sizeof parm);
+
+ *r_fp = NULL;
+
+ err = open_context (ctrl, &ctx);
+ if (err)
+ return err;
+
+ /* Lump all patterns into one string. */
+ init_membuf (&mb, 1024);
+ put_membuf_str (&mb, "KS_GET --");
+ for (idx=0; pattern[idx]; idx++)
+ {
+ put_membuf (&mb, " ", 1); /* Append Delimiter. */
+ put_membuf_str (&mb, pattern[idx]);
+ }
+ put_membuf (&mb, "", 1); /* Append Nul. */
+ line = get_membuf (&mb, &linelen);
+ if (!line)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ if (linelen + 2 >= ASSUAN_LINELENGTH)
+ {
+ err = gpg_error (GPG_ERR_TOO_MANY);
+ goto leave;
+ }
+
+ parm.memfp = es_fopenmem (0, "rwb");
+ if (!parm.memfp)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ err = assuan_transact (ctx, line, ks_get_data_cb, &parm,
+ NULL, NULL, NULL, NULL);
+ if (err)
+ goto leave;
+
+ es_rewind (parm.memfp);
+ *r_fp = parm.memfp;
+ parm.memfp = NULL;
+
+ leave:
+ es_fclose (parm.memfp);
+ xfree (line);
+ close_context (ctrl, ctx);
+ return err;
+}
+
+
+
+/* Handle the KS_PUT inquiries. */
+static gpg_error_t
+ks_put_inq_cb (void *opaque, const char *line)
+{
+ struct ks_put_parm_s *parm = opaque;
+ gpg_error_t err = 0;
+
+ if (!strncmp (line, "KEYBLOCK", 8) && (line[8] == ' ' || !line[8]))
+ {
+ if (parm->data)
+ err = assuan_send_data (parm->ctx, parm->data, parm->datalen);
+ }
+ else if (!strncmp (line, "KEYBLOCK_INFO", 13) && (line[13]==' ' || !line[13]))
+ {
+ kbnode_t node;
+ estream_t fp;
+
+ /* Parse the keyblock and send info lines back to the server. */
+ fp = es_fopenmem (0, "rw");
+ if (!fp)
+ err = gpg_error_from_syserror ();
+
+ for (node = parm->keyblock; !err && node; node=node->next)
+ {
+ switch(node->pkt->pkttype)
+ {
+ case PKT_PUBLIC_KEY:
+ case PKT_PUBLIC_SUBKEY:
+ {
+ PKT_public_key *pk = node->pkt->pkt.public_key;
+
+ keyid_from_pk (pk, NULL);
+
+ es_fprintf (fp, "%s:%08lX%08lX:%u:%u:%u:%u:%s%s:\n",
+ node->pkt->pkttype==PKT_PUBLIC_KEY? "pub" : "sub",
+ (ulong)pk->keyid[0], (ulong)pk->keyid[1],
+ pk->pubkey_algo,
+ nbits_from_pk (pk),
+ pk->timestamp,
+ pk->expiredate,
+ pk->flags.revoked? "r":"",
+ pk->has_expired? "e":"");
+ }
+ break;
+
+ case PKT_USER_ID:
+ {
+ PKT_user_id *uid = node->pkt->pkt.user_id;
+ int r;
+
+ if (!uid->attrib_data)
+ {
+ es_fprintf (fp, "uid:");
+
+ /* Quote ':', '%', and any 8-bit characters. */
+ for (r=0; r < uid->len; r++)
+ {
+ if (uid->name[r] == ':'
+ || uid->name[r]== '%'
+ || (uid->name[r]&0x80))
+ es_fprintf (fp, "%%%02X", (byte)uid->name[r]);
+ else
+ es_putc (uid->name[r], fp);
+ }
+
+ es_fprintf (fp, ":%u:%u:%s%s:\n",
+ uid->created,uid->expiredate,
+ uid->is_revoked? "r":"",
+ uid->is_expired? "e":"");
+ }
+ }
+ break;
+
+ /* This bit is really for the benefit of people who
+ store their keys in LDAP servers. It makes it easy
+ to do queries for things like "all keys signed by
+ Isabella". */
+ case PKT_SIGNATURE:
+ {
+ PKT_signature *sig = node->pkt->pkt.signature;
+
+ if (IS_UID_SIG (sig))
+ {
+ es_fprintf (fp, "sig:%08lX%08lX:%X:%u:%u:\n",
+ (ulong)sig->keyid[0],(ulong)sig->keyid[1],
+ sig->sig_class, sig->timestamp,
+ sig->expiredate);
+ }
+ }
+ break;
+
+ default:
+ continue;
+ }
+ /* Given that the last operation was an es_fprintf we should
+ get the correct ERRNO if ferror indicates an error. */
+ if (es_ferror (fp))
+ err = gpg_error_from_syserror ();
+ }
+
+ /* Without an error and if we have an keyblock at all, send the
+ data back. */
+ if (!err && parm->keyblock)
+ {
+ int rc;
+ char buffer[512];
+ size_t nread;
+
+ es_rewind (fp);
+ while (!(rc=es_read (fp, buffer, sizeof buffer, &nread)) && nread)
+ {
+ err = assuan_send_data (parm->ctx, buffer, nread);
+ if (err)
+ break;
+ }
+ if (!err && rc)
+ err = gpg_error_from_syserror ();
+ }
+ es_fclose (fp);
+ }
+ else
+ return gpg_error (GPG_ERR_ASS_UNKNOWN_INQUIRE);
+
+ return err;
+}
+
+
+/* Send a key to the configured server. {DATA,DATLEN} contains the
+ key in OpenPGP binary transport format. If KEYBLOCK is not NULL it
+ has the internal representaion of that key; this is for example
+ used to convey meta data to LDAP keyservers. */
+gpg_error_t
+gpg_dirmngr_ks_put (ctrl_t ctrl, void *data, size_t datalen, kbnode_t keyblock)
+{
+ gpg_error_t err;
+ assuan_context_t ctx;
+ struct ks_put_parm_s parm;
+
+ memset (&parm, 0, sizeof parm);
+
+ /* We are going to parse the keyblock, thus we better make sure the
+ all information is readily available. */
+ if (keyblock)
+ merge_keys_and_selfsig (keyblock);
+
+ err = open_context (ctrl, &ctx);
+ if (err)
+ return err;
+
+ parm.ctx = ctx;
+ parm.keyblock = keyblock;
+ parm.data = data;
+ parm.datalen = datalen;
+
+ err = assuan_transact (ctx, "KS_PUT", NULL, NULL,
+ ks_put_inq_cb, &parm, NULL, NULL);
+
+ close_context (ctrl, ctx);
+ return err;
+}
diff --git a/g10/call-dirmngr.h b/g10/call-dirmngr.h
new file mode 100644
index 000000000..9523bf568
--- /dev/null
+++ b/g10/call-dirmngr.h
@@ -0,0 +1,32 @@
+/* call-dirmngr.h - GPG operations to the Dirmngr
+ * Copyright (C) 2011 Free Software Foundation, Inc.
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+#ifndef GNUPG_G10_CALL_DIRMNGR_H
+#define GNUPG_G10_CALL_DIRMNGR_H
+
+void gpg_dirmngr_deinit_session_data (ctrl_t ctrl);
+
+gpg_error_t gpg_dirmngr_ks_search (ctrl_t ctrl, const char *searchstr,
+ gpg_error_t (*cb)(void*, char *),
+ void *cb_value);
+gpg_error_t gpg_dirmngr_ks_get (ctrl_t ctrl, char *pattern[], estream_t *r_fp);
+gpg_error_t gpg_dirmngr_ks_put (ctrl_t ctrl, void *data, size_t datalen,
+ kbnode_t keyblock);
+
+
+#endif /*GNUPG_G10_CALL_DIRMNGR_H*/
diff --git a/g10/export.c b/g10/export.c
index 74a7b0c51..1eb0baa8b 100644
--- a/g10/export.c
+++ b/g10/export.c
@@ -114,6 +114,60 @@ export_pubkeys_stream (ctrl_t ctrl, iobuf_t out, strlist_t users,
return rc;
}
+
+/*
+ * Export a single key into a memory buffer.
+ */
+gpg_error_t
+export_pubkey_buffer (ctrl_t ctrl, const char *keyspec, unsigned int options,
+ kbnode_t *r_keyblock, void **r_data, size_t *r_datalen)
+{
+ gpg_error_t err;
+ iobuf_t iobuf;
+ int any;
+ strlist_t helplist;
+
+ *r_keyblock = NULL;
+ *r_data = NULL;
+ *r_datalen = 0;
+
+ helplist = NULL;
+ if (!add_to_strlist_try (&helplist, keyspec))
+ return gpg_error_from_syserror ();
+
+ iobuf = iobuf_temp ();
+ err = do_export_stream (ctrl, iobuf, helplist, 0, r_keyblock, options, &any);
+ if (!err && !any)
+ err = gpg_error (GPG_ERR_NOT_FOUND);
+ if (!err)
+ {
+ const void *src;
+ size_t datalen;
+
+ iobuf_flush_temp (iobuf);
+ src = iobuf_get_temp_buffer (iobuf);
+ datalen = iobuf_get_temp_length (iobuf);
+ if (!datalen)
+ err = gpg_error (GPG_ERR_NO_PUBKEY);
+ else if (!(*r_data = xtrymalloc (datalen)))
+ err = gpg_error_from_syserror ();
+ else
+ {
+ memcpy (*r_data, src, datalen);
+ *r_datalen = datalen;
+ }
+ }
+ iobuf_close (iobuf);
+ free_strlist (helplist);
+ if (err && *r_keyblock)
+ {
+ release_kbnode (*r_keyblock);
+ *r_keyblock = NULL;
+ }
+ return err;
+}
+
+
int
export_seckeys (ctrl_t ctrl, strlist_t users )
{
diff --git a/g10/gpg.c b/g10/gpg.c
index 3794aa2b7..47e8b361f 100644
--- a/g10/gpg.c
+++ b/g10/gpg.c
@@ -1,6 +1,6 @@
/* gpg.c - The GnuPG utility (main for gpg)
- * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
- * 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
+ * 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
@@ -54,6 +54,7 @@
#include "exec.h"
#include "gc-opt-flags.h"
#include "asshelp.h"
+#include "call-dirmngr.h"
#if defined(HAVE_DOSISH_SYSTEM) || defined(__CYGWIN__)
#define MY_O_BINARY O_BINARY
@@ -1822,7 +1823,7 @@ gpg_init_default_ctrl (ctrl_t ctrl)
static void
gpg_deinit_default_ctrl (ctrl_t ctrl)
{
- (void)ctrl;
+ gpg_dirmngr_deinit_session_data (ctrl);
}
@@ -2658,15 +2659,15 @@ main (int argc, char **argv)
break;
case oKeyServer:
{
- struct keyserver_spec *keyserver;
- keyserver=parse_keyserver_uri(pargs.r.ret_str,0,
- configname,configlineno);
- if(!keyserver)
- log_error(_("could not parse keyserver URL\n"));
+ keyserver_spec_t keyserver;
+ keyserver = parse_keyserver_uri (pargs.r.ret_str,0,
+ configname,configlineno);
+ if (!keyserver)
+ log_error (_("could not parse keyserver URL\n"));
else
{
- keyserver->next=opt.keyserver;
- opt.keyserver=keyserver;
+ keyserver->next = opt.keyserver;
+ opt.keyserver = keyserver;
}
}
break;
@@ -2853,14 +2854,14 @@ main (int argc, char **argv)
break;
case oDefaultKeyserverURL:
{
- struct keyserver_spec *keyserver;
- keyserver=parse_keyserver_uri(pargs.r.ret_str,1,
- configname,configlineno);
- if(!keyserver)
- log_error(_("could not parse keyserver URL\n"));
+ keyserver_spec_t keyserver;
+ keyserver = parse_keyserver_uri (pargs.r.ret_str,1,
+ configname,configlineno);
+ if (!keyserver)
+ log_error (_("could not parse keyserver URL\n"));
else
- free_keyserver_spec(keyserver);
-
+ free_keyserver_spec (keyserver);
+
opt.def_keyserver_url = pargs.r.ret_str;
}
break;
@@ -3751,12 +3752,12 @@ main (int argc, char **argv)
case aSearchKeys:
sl = NULL;
- for( ; argc; argc--, argv++ )
- append_to_strlist2( &sl, *argv, utf8_strings );
+ for (; argc; argc--, argv++)
+ append_to_strlist2 (&sl, *argv, utf8_strings);
rc = keyserver_search (ctrl, sl);
- if(rc)
- log_error(_("keyserver search failed: %s\n"),g10_errstr(rc));
- free_strlist(sl);
+ if (rc)
+ log_error (_("keyserver search failed: %s\n"), gpg_strerror (rc));
+ free_strlist (sl);
break;
case aRefreshKeys:
diff --git a/g10/gpg.h b/g10/gpg.h
index 1d645ea25..29db15a45 100644
--- a/g10/gpg.h
+++ b/g10/gpg.h
@@ -48,6 +48,10 @@
/* Object used to keep state locally to server.c . */
struct server_local_s;
+/* Object used to keep state locally to call-dirmngr.c . */
+struct dirmngr_local_s;
+typedef struct dirmngr_local_s *dirmngr_local_t;
+
/* Object used to describe a keyblok node. */
typedef struct kbnode_struct *KBNODE;
typedef struct kbnode_struct *kbnode_t;
@@ -58,7 +62,11 @@ typedef struct kbnode_struct *kbnode_t;
gpg_init_default_ctrl(). */
struct server_control_s
{
+ /* Local data for server.c */
struct server_local_s *server_local;
+
+ /* Local data for call-dirmngr.c */
+ dirmngr_local_t dirmngr_local;
};
diff --git a/g10/import.c b/g10/import.c
index 31160c33e..88abafd6a 100644
--- a/g10/import.c
+++ b/g10/import.c
@@ -243,6 +243,32 @@ import_keys_stream (ctrl_t ctrl, IOBUF inp, void *stats_handle,
fpr, fpr_len, options);
}
+
+/* Variant of import_keys_stream reading from an estream_t. */
+int
+import_keys_es_stream (ctrl_t ctrl, estream_t fp, void *stats_handle,
+ unsigned char **fpr, size_t *fpr_len,
+ unsigned int options)
+{
+ int rc;
+ iobuf_t inp;
+
+ inp = iobuf_esopen (fp, "r", 1);
+ if (!inp)
+ {
+ rc = gpg_error_from_syserror ();
+ log_error ("iobuf_esopen failed: %s\n", gpg_strerror (rc));
+ return rc;
+ }
+
+ rc = import_keys_internal (ctrl, inp, NULL, 0, stats_handle,
+ fpr, fpr_len, options);
+
+ iobuf_close (inp);
+ return rc;
+}
+
+
static int
import (ctrl_t ctrl, IOBUF inp, const char* fname,struct stats_s *stats,
unsigned char **fpr,size_t *fpr_len,unsigned int options )
diff --git a/g10/keyserver-internal.h b/g10/keyserver-internal.h
index cbf3c04a8..2b1b64e35 100644
--- a/g10/keyserver-internal.h
+++ b/g10/keyserver-internal.h
@@ -40,7 +40,7 @@ int keyserver_import_fprint (ctrl_t ctrl, const byte *fprint,size_t fprint_len,
int keyserver_import_keyid (ctrl_t ctrl, u32 *keyid,
struct keyserver_spec *keyserver);
int keyserver_refresh (ctrl_t ctrl, strlist_t users);
-int keyserver_search (ctrl_t ctrl, strlist_t tokens);
+gpg_error_t keyserver_search (ctrl_t ctrl, strlist_t tokens);
int keyserver_fetch (ctrl_t ctrl, strlist_t urilist);
int keyserver_import_cert (ctrl_t ctrl, const char *name,
unsigned char **fpr,size_t *fpr_len);
diff --git a/g10/keyserver.c b/g10/keyserver.c
index 422e62e78..2f055ada5 100644
--- a/g10/keyserver.c
+++ b/g10/keyserver.c
@@ -1,6 +1,6 @@
/* keyserver.c - generic keyserver code
* Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008,
- * 2009 Free Software Foundation, Inc.
+ * 2009, 2011 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
@@ -18,6 +18,9 @@
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+/* !!! FIXME: Replace all printf by es_printf. FIXME !!! */
+
+
#include <config.h>
#include <ctype.h>
#include <stdio.h>
@@ -45,7 +48,8 @@
#ifdef USE_DNS_SRV
#include "srv.h"
#endif
-
+#include "membuf.h"
+#include "call-dirmngr.h"
#ifdef HAVE_W32_SYSTEM
/* It seems Vista doesn't grok X_OK and so fails access() tests.
@@ -65,6 +69,24 @@ struct keyrec
unsigned int lines;
};
+/* Parameters for the search line handler. */
+struct search_line_handler_parm_s
+{
+ ctrl_t ctrl; /* The session control structure. */
+ char *searchstr_disp; /* Native encoded search string or NULL. */
+ KEYDB_SEARCH_DESC *desc; /* Array with search descriptions. */
+ int count; /* Number of keys we are currently prepared to
+ handle. This is the size of the DESC array. If
+ it is too small, it will grow safely. */
+ int validcount; /* Enable the "Key x-y of z" messages. */
+ int nkeys; /* Number of processed records. */
+ int any_lines; /* At least one line has been processed. */
+ unsigned int numlines; /* Counter for displayed lines. */
+ int eof_seen; /* EOF encountered. */
+ int not_found; /* Set if no keys have been found. */
+};
+
+
enum ks_action {KS_UNKNOWN=0,KS_GET,KS_GETNAME,KS_SEND,KS_SEARCH};
static struct parse_options keyserver_opts[]=
@@ -89,10 +111,12 @@ static struct parse_options keyserver_opts[]=
{NULL,0,NULL,NULL}
};
-static int keyserver_work (ctrl_t ctrl, enum ks_action action,strlist_t list,
- KEYDB_SEARCH_DESC *desc,int count,
- unsigned char **fpr,size_t *fpr_len,
- struct keyserver_spec *keyserver);
+static gpg_error_t keyserver_get (ctrl_t ctrl,
+ KEYDB_SEARCH_DESC *desc, int ndesc,
+ struct keyserver_spec *keyserver);
+static gpg_error_t keyserver_put (ctrl_t ctrl, strlist_t keyspecs,
+ struct keyserver_spec *keyserver);
+
/* Reasonable guess */
#define DEFAULT_MAX_CERT_SIZE 16384
@@ -236,9 +260,9 @@ keyserver_match(struct keyserver_spec *spec)
parser any longer so it can be removed, or at least moved to
keyserver/ksutil.c for limited use in gpgkeys_ldap or the like. */
-struct keyserver_spec *
-parse_keyserver_uri(const char *string,int require_scheme,
- const char *configname,unsigned int configlineno)
+keyserver_spec_t
+parse_keyserver_uri (const char *string,int require_scheme,
+ const char *configname,unsigned int configlineno)
{
int assume_hkp=0;
struct keyserver_spec *keyserver;
@@ -555,6 +579,8 @@ print_keyrec(int number,struct keyrec *keyrec)
static struct keyrec *
parse_keyrec(char *keystring)
{
+ /* FIXME: Remove the static and put the data into the parms we use
+ for the caller anyway. */
static struct keyrec *work=NULL;
struct keyrec *ret=NULL;
char *record;
@@ -583,12 +609,7 @@ parse_keyrec(char *keystring)
work->uidbuf=iobuf_temp();
}
- /* Remove trailing whitespace */
- for(i=strlen(keystring);i>0;i--)
- if(ascii_isspace(keystring[i-1]))
- keystring[i-1]='\0';
- else
- break;
+ trim_trailing_ws (keystring, strlen (keystring));
if((record=strsep(&keystring,":"))==NULL)
return ret;
@@ -664,7 +685,7 @@ parse_keyrec(char *keystring)
case 'R':
work->flags|=1;
break;
-
+
case 'd':
case 'D':
work->flags|=2;
@@ -727,882 +748,247 @@ parse_keyrec(char *keystring)
return ret;
}
-/* TODO: do this as a list sent to keyserver_work rather than calling
- it once for each key to get the correct counts after the import
- (cosmetics, really) and to better take advantage of the keyservers
- that can do multiple fetches in one go (LDAP). */
-static int
-show_prompt (ctrl_t ctrl,
- KEYDB_SEARCH_DESC *desc,int numdesc,int count,const char *search)
+/* Show a prompt and allow the user to select keys for retrieval. */
+static gpg_error_t
+show_prompt (ctrl_t ctrl, KEYDB_SEARCH_DESC *desc, int numdesc,
+ int count, const char *search)
{
- char *answer;
+ gpg_error_t err;
+ char *answer = NULL;
fflush (stdout);
- if(count && opt.command_fd==-1)
+ if (count && opt.command_fd == -1)
{
- static int from=1;
- tty_printf("Keys %d-%d of %d for \"%s\". ",from,numdesc,count,search);
- from=numdesc+1;
+ static int from = 1;
+ tty_printf ("Keys %d-%d of %d for \"%s\". ",
+ from, numdesc, count, search);
+ from = numdesc + 1;
}
- answer=cpr_get_no_help("keysearch.prompt",
- _("Enter number(s), N)ext, or Q)uit > "));
+ again:
+ err = 0;
+ xfree (answer);
+ answer = cpr_get_no_help ("keysearch.prompt",
+ _("Enter number(s), N)ext, or Q)uit > "));
/* control-d */
- if(answer[0]=='\x04')
+ if (answer[0]=='\x04')
{
- printf("Q\n");
- answer[0]='q';
+ tty_printf ("Q\n");
+ answer[0] = 'q';
}
- if(answer[0]=='q' || answer[0]=='Q')
- {
- xfree(answer);
- return 1;
- }
- else if(atoi(answer)>=1 && atoi(answer)<=numdesc)
+ if (answer[0]=='q' || answer[0]=='Q')
+ err = gpg_error (GPG_ERR_CANCELED);
+ else if (atoi (answer) >= 1 && atoi (answer) <= numdesc)
{
- char *split=answer,*num;
-
- while((num=strsep(&split," ,"))!=NULL)
- if(atoi(num)>=1 && atoi(num)<=numdesc)
- keyserver_work (ctrl, KS_GET,NULL,&desc[atoi(num)-1],1,
- NULL,NULL,opt.keyserver);
-
- xfree(answer);
- return 1;
- }
-
- return 0;
-}
-
-/* Count and searchstr are just for cosmetics. If the count is too
- small, it will grow safely. If negative it disables the "Key x-y
- of z" messages. searchstr should be UTF-8 (rather than native). */
-static void
-keyserver_search_prompt (ctrl_t ctrl, IOBUF buffer,const char *searchstr)
-{
- int i=0,validcount=0,started=0,header=0,count=1;
- unsigned int maxlen,buflen,numlines=0;
- KEYDB_SEARCH_DESC *desc;
- byte *line=NULL;
- char *localstr=NULL;
-
- if(searchstr)
- localstr=utf8_to_native(searchstr,strlen(searchstr),0);
-
- desc=xmalloc(count*sizeof(KEYDB_SEARCH_DESC));
-
- for(;;)
- {
- struct keyrec *keyrec;
- int rl;
-
- maxlen=1024;
- rl=iobuf_read_line(buffer,&line,&buflen,&maxlen);
-
- if(opt.with_colons)
- {
- if(!header && ascii_strncasecmp("SEARCH ",line,7)==0
- && ascii_strncasecmp(" BEGIN",&line[strlen(line)-7],6)==0)
- {
- header=1;
- continue;
- }
- else if(ascii_strncasecmp("SEARCH ",line,7)==0
- && ascii_strncasecmp(" END",&line[strlen(line)-5],4)==0)
- continue;
-
- printf("%s",line);
- }
-
- /* Look for an info: line. The only current info: values
- defined are the version and key count. */
- if(!started && rl>0 && ascii_strncasecmp("info:",line,5)==0)
- {
- char *tok,*str=&line[5];
-
- if((tok=strsep(&str,":"))!=NULL)
- {
- int version;
-
- if(sscanf(tok,"%d",&version)!=1)
- version=1;
-
- if(version!=1)
- {
- log_error(_("invalid keyserver protocol "
- "(us %d!=handler %d)\n"),1,version);
- break;
- }
- }
-
- if((tok=strsep(&str,":"))!=NULL && sscanf(tok,"%d",&count)==1)
- {
- if(count==0)
- goto notfound;
- else if(count<0)
- count=10;
- else
- validcount=1;
-
- desc=xrealloc(desc,count*sizeof(KEYDB_SEARCH_DESC));
- }
-
- started=1;
- continue;
- }
-
- if(rl==0)
- {
- keyrec=parse_keyrec(NULL);
-
- if(keyrec==NULL)
- {
- if(i==0)
- {
- count=0;
- break;
- }
-
- if(i!=count)
- validcount=0;
+ char *split = answer;
+ char *num;
+ int numarray[50];
+ int numidx = 0;
+ int idx;
+
+ while ((num = strsep (&split, " ,")))
+ if (atoi (num) >= 1 && atoi (num) <= numdesc)
+ {
+ if (numidx >= DIM (numarray))
+ {
+ tty_printf ("Too many keys selected\n");
+ goto again;
+ }
+ numarray[numidx++] = atoi (num);
+ }
+
+ if (!numidx)
+ goto again;
- if (opt.with_colons && opt.batch)
- break;
-
- for(;;)
- {
- if (show_prompt (ctrl, desc, i, validcount?count:0, localstr))
- break;
- validcount=0;
- }
-
- break;
- }
- }
- else
- keyrec=parse_keyrec(line);
-
- if(i==count)
- {
- /* keyserver helper sent more keys than they claimed in the
- info: line. */
- count+=10;
- desc=xrealloc(desc,count*sizeof(KEYDB_SEARCH_DESC));
- validcount=0;
- }
-
- if(keyrec)
- {
- desc[i]=keyrec->desc;
-
- if(!opt.with_colons)
- {
- /* screen_lines - 1 for the prompt. */
- if(numlines+keyrec->lines>opt.screen_lines-1)
- {
- if (show_prompt (ctrl, desc, i, validcount?count:0, localstr))
- break;
- else
- numlines=0;
- }
-
- print_keyrec(i+1,keyrec);
- }
-
- numlines+=keyrec->lines;
- iobuf_close(keyrec->uidbuf);
- xfree(keyrec);
-
- started=1;
- i++;
- }
- }
-
- notfound:
- /* Leave this commented out or now, and perhaps for a very long
- time. All HKPish servers return HTML error messages for
- no-key-found. */
- /*
- if(!started)
- log_info(_("keyserver does not support searching\n"));
- else
- */
- if(count==0)
- {
- if(localstr)
- log_info(_("key \"%s\" not found on keyserver\n"),localstr);
- else
- log_info(_("key not found on keyserver\n"));
+ {
+ KEYDB_SEARCH_DESC *selarray;
+
+ selarray = xtrymalloc (numidx * sizeof *selarray);
+ if (!selarray)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ for (idx = 0; idx < numidx; idx++)
+ selarray[idx] = desc[numarray[idx]-1];
+ err = keyserver_get (ctrl, selarray, numidx, NULL);
+ xfree (selarray);
+ }
}
- xfree(localstr);
- xfree(desc);
- xfree(line);
-}
-
-/* We sometimes want to use a different gpgkeys_xxx for a given
- protocol (for example, ldaps is handled by gpgkeys_ldap). Map
- these here. */
-static const char *
-keyserver_typemap(const char *type)
-{
- if(strcmp(type,"ldaps")==0)
- return "ldap";
- else if(strcmp(type,"hkps")==0)
- return "hkp";
- else
- return type;
+ leave:
+ xfree (answer);
+ return err;
}
-/* The PGP LDAP and the curl fetch-a-LDAP-object methodologies are
- sufficiently different that we can't use curl to do LDAP. */
-static int
-direct_uri_map(const char *scheme,unsigned int is_direct)
-{
- if(is_direct && strcmp(scheme,"ldap")==0)
- return 1;
-
- return 0;
-}
-#if GNUPG_MAJOR_VERSION == 2
-#define GPGKEYS_PREFIX "gpg2keys_"
-#else
-#define GPGKEYS_PREFIX "gpgkeys_"
-#endif
-#define GPGKEYS_CURL GPGKEYS_PREFIX "curl" EXEEXT
-#define GPGKEYS_PREFIX_LEN (strlen(GPGKEYS_CURL))
-#define KEYSERVER_ARGS_KEEP " -o \"%O\" \"%I\""
-#define KEYSERVER_ARGS_NOKEEP " -o \"%o\" \"%i\""
-
-static int
-keyserver_spawn (ctrl_t ctrl,
- enum ks_action action,strlist_t list,KEYDB_SEARCH_DESC *desc,
- int count,int *prog,unsigned char **fpr,size_t *fpr_len,
- struct keyserver_spec *keyserver)
+/* This is a callback used by call-dirmngr.c to process the result of
+ KS_SEARCH command. LINE is the actual data line received with all
+ escaping removed and guaranteed to be exactly one line with
+ stripped LF; an EOF is indicated by LINE passed as NULL. LINE may
+ be modified after return. */
+static gpg_error_t
+search_line_handler (void *opaque, char *line)
{
- int ret=0,i,gotversion=0,outofband=0;
- strlist_t temp;
- unsigned int maxlen,buflen;
- char *command,*end,*searchstr=NULL;
- byte *line=NULL;
- struct exec_info *spawn;
- const char *scheme;
- const char *libexecdir = gnupg_libexecdir ();
+ struct search_line_handler_parm_s *parm = opaque;
+ gpg_error_t err = 0;
+ struct keyrec *keyrec;
- assert(keyserver);
-
-#ifdef EXEC_TEMPFILE_ONLY
- opt.keyserver_options.options|=KEYSERVER_USE_TEMP_FILES;
-#endif
-
- /* Build the filename for the helper to execute */
- scheme=keyserver_typemap(keyserver->scheme);
-
-#ifdef DISABLE_KEYSERVER_PATH
- /* Destroy any path we might have. This is a little tricky,
- portability-wise. It's not correct to delete the PATH
- environment variable, as that may fall back to a system built-in
- PATH. Similarly, it is not correct to set PATH to the null
- string (PATH="") since this actually deletes the PATH environment
- variable under MinGW. The safest thing to do here is to force
- PATH to be GNUPG_LIBEXECDIR. All this is not that meaningful on
- Unix-like systems (since we're going to give a full path to
- gpgkeys_foo), but on W32 it prevents loading any DLLs from
- directories in %PATH%.
-
- After some more thinking about this we came to the conclusion
- that it is better to load the helpers from the directory where
- the program of this process lives. Fortunately Windows provides
- a way to retrieve this and our gnupg_libexecdir function has been
- modified to return just this. Setting the exec-path is not
- anymore required.
- set_exec_path(libexecdir);
- */
-#else
- if(opt.exec_path_set)
+ if (parm->eof_seen && line)
{
- /* If exec-path was set, and DISABLE_KEYSERVER_PATH is
- undefined, then don't specify a full path to gpgkeys_foo, so
- that the PATH can work. */
- command=xmalloc(GPGKEYS_PREFIX_LEN+strlen(scheme)+3+strlen(EXEEXT)+1);
- command[0]='\0';
+ log_debug ("ooops: unexpected data after EOF\n");
+ line = NULL;
}
- else
-#endif
- {
- /* Specify a full path to gpgkeys_foo. */
- command=xmalloc(strlen(libexecdir)+strlen(DIRSEP_S)+
- GPGKEYS_PREFIX_LEN+strlen(scheme)+3+strlen(EXEEXT)+1);
- strcpy(command,libexecdir);
- strcat(command,DIRSEP_S);
- }
-
- end=command+strlen(command);
-
- /* Build a path for the keyserver helper. If it is direct_uri
- (i.e. an object fetch and not a keyserver), then add "_uri" to
- the end to distinguish the keyserver helper from an object
- fetcher that can speak that protocol (this is a problem for
- LDAP). */
-
- strcat(command,GPGKEYS_PREFIX);
- strcat(command,scheme);
-
- /* This "_uri" thing is in case we need to call a direct handler
- instead of the keyserver handler. This lets us use gpgkeys_curl
- or gpgkeys_ldap_uri (we don't provide it, but a user might)
- instead of gpgkeys_ldap to fetch things like
- ldap://keyserver.pgp.com/o=PGP%20keys?pgpkey?sub?pgpkeyid=99242560 */
- if(direct_uri_map(scheme,keyserver->flags.direct_uri))
- strcat(command,"_uri");
-
- strcat(command,EXEEXT);
-
- /* Can we execute it? If not, try curl as our catchall. */
- if(path_access(command,X_OK)!=0)
- strcpy(end,GPGKEYS_CURL);
-
- if(opt.keyserver_options.options&KEYSERVER_USE_TEMP_FILES)
+ /* Print the received line. */
+ if (opt.with_colons && line)
{
- if(opt.keyserver_options.options&KEYSERVER_KEEP_TEMP_FILES)
- {
- command=xrealloc(command,strlen(command)+
- strlen(KEYSERVER_ARGS_KEEP)+1);
- strcat(command,KEYSERVER_ARGS_KEEP);
- }
- else
- {
- command=xrealloc(command,strlen(command)+
- strlen(KEYSERVER_ARGS_NOKEEP)+1);
- strcat(command,KEYSERVER_ARGS_NOKEEP);
- }
-
- ret=exec_write(&spawn,NULL,command,NULL,0,0);
+ log_debug ("%s\n",line);
}
- else
- ret=exec_write(&spawn,command,NULL,NULL,0,0);
- xfree(command);
-
- if(ret)
- return ret;
-
- fprintf(spawn->tochild,
- "# This is a GnuPG %s keyserver communications file\n",VERSION);
- fprintf(spawn->tochild,"VERSION %d\n",KEYSERVER_PROTO_VERSION);
- fprintf(spawn->tochild,"PROGRAM %s\n",VERSION);
- fprintf(spawn->tochild,"SCHEME %s\n",keyserver->scheme);
-
- if(keyserver->opaque)
- fprintf(spawn->tochild,"OPAQUE %s\n",keyserver->opaque);
- else
+ /* Look for an info: line. The only current info: values defined
+ are the version and key count. */
+ if (line && !parm->any_lines && !ascii_strncasecmp ("info:", line, 5))
{
- if(keyserver->auth)
- fprintf(spawn->tochild,"AUTH %s\n",keyserver->auth);
-
- if(keyserver->host)
- fprintf(spawn->tochild,"HOST %s\n",keyserver->host);
-
- if(keyserver->port)
- fprintf(spawn->tochild,"PORT %s\n",keyserver->port);
-
- if(keyserver->path)
- fprintf(spawn->tochild,"PATH %s\n",keyserver->path);
- }
-
- /* Write global options */
-
- for(temp=opt.keyserver_options.other;temp;temp=temp->next)
- fprintf(spawn->tochild,"OPTION %s\n",temp->d);
-
- /* Write per-keyserver options */
-
- for(temp=keyserver->options;temp;temp=temp->next)
- fprintf(spawn->tochild,"OPTION %s\n",temp->d);
-
- switch(action)
- {
- case KS_GET:
- {
- fprintf(spawn->tochild,"COMMAND GET\n\n");
-
- /* Which keys do we want? */
-
- for(i=0;i<count;i++)
- {
- int quiet=0;
-
- if(desc[i].mode==KEYDB_SEARCH_MODE_FPR20)
- {
- int f;
-
- fprintf(spawn->tochild,"0x");
-
- for(f=0;f<MAX_FINGERPRINT_LEN;f++)
- fprintf(spawn->tochild,"%02X",desc[i].u.fpr[f]);
-
- fprintf(spawn->tochild,"\n");
- }
- else if(desc[i].mode==KEYDB_SEARCH_MODE_FPR16)
- {
- int f;
-
- fprintf(spawn->tochild,"0x");
-
- for(f=0;f<16;f++)
- fprintf(spawn->tochild,"%02X",desc[i].u.fpr[f]);
-
- fprintf(spawn->tochild,"\n");
- }
- else if(desc[i].mode==KEYDB_SEARCH_MODE_LONG_KID)
- fprintf(spawn->tochild,"0x%08lX%08lX\n",
- (ulong)desc[i].u.kid[0],
- (ulong)desc[i].u.kid[1]);
- else if(desc[i].mode==KEYDB_SEARCH_MODE_SHORT_KID)
- fprintf(spawn->tochild,"0x%08lX\n",
- (ulong)desc[i].u.kid[1]);
- else if(desc[i].mode==KEYDB_SEARCH_MODE_EXACT)
- {
- fprintf(spawn->tochild,"0x0000000000000000\n");
- quiet=1;
- }
- else if(desc[i].mode==KEYDB_SEARCH_MODE_NONE)
- continue;
- else
- BUG();
-
- if(!quiet)
- {
- if(keyserver->host)
- log_info(_("requesting key %s from %s server %s\n"),
- keystr_from_desc(&desc[i]),
- keyserver->scheme,keyserver->host);
- else
- log_info(_("requesting key %s from %s\n"),
- keystr_from_desc(&desc[i]),keyserver->uri);
- }
- }
-
- fprintf(spawn->tochild,"\n");
-
- break;
- }
-
- case KS_GETNAME:
- {
- strlist_t key;
-
- fprintf(spawn->tochild,"COMMAND GETNAME\n\n");
-
- /* Which names do we want? */
-
- for(key=list;key!=NULL;key=key->next)
- fprintf(spawn->tochild,"%s\n",key->d);
-
- fprintf(spawn->tochild,"\n");
-
- if(keyserver->host)
- log_info(_("searching for names from %s server %s\n"),
- keyserver->scheme,keyserver->host);
- else
- log_info(_("searching for names from %s\n"),keyserver->uri);
-
- break;
- }
-
- case KS_SEND:
- {
- strlist_t key;
-
- /* Note the extra \n here to send an empty keylist block */
- fprintf(spawn->tochild,"COMMAND SEND\n\n\n");
-
- for(key=list;key!=NULL;key=key->next)
- {
- armor_filter_context_t *afx;
- IOBUF buffer = iobuf_temp ();
- KBNODE block;
-
- temp=NULL;
- add_to_strlist(&temp,key->d);
-
- afx = new_armor_context ();
- afx->what = 1;
- /* Tell the armor filter to use Unix-style \n line
- endings, since we're going to fprintf this to a file
- that (on Win32) is open in text mode. The win32 stdio
- will transform the \n to \r\n and we'll end up with the
- proper line endings on win32. This is a no-op on
- Unix. */
- afx->eol[0] = '\n';
- push_armor_filter (afx, buffer);
- release_armor_context (afx);
-
- /* TODO: Remove Comment: lines from keys exported this
- way? */
-
- if(export_pubkeys_stream (ctrl, buffer,temp,&block,
- opt.keyserver_options.export_options)==-1)
- iobuf_close(buffer);
- else
- {
- KBNODE node;
-
- iobuf_flush_temp(buffer);
-
- merge_keys_and_selfsig(block);
-
- fprintf(spawn->tochild,"INFO %08lX%08lX BEGIN\n",
- (ulong)block->pkt->pkt.public_key->keyid[0],
- (ulong)block->pkt->pkt.public_key->keyid[1]);
-
- for(node=block;node;node=node->next)
- {
- switch(node->pkt->pkttype)
- {
- default:
- continue;
-
- case PKT_PUBLIC_KEY:
- case PKT_PUBLIC_SUBKEY:
- {
- PKT_public_key *pk=node->pkt->pkt.public_key;
-
- keyid_from_pk(pk,NULL);
-
- fprintf(spawn->tochild,"%sb:%08lX%08lX:%u:%u:%u:%u:",
- node->pkt->pkttype==PKT_PUBLIC_KEY?"pu":"su",
- (ulong)pk->keyid[0],(ulong)pk->keyid[1],
- pk->pubkey_algo,
- nbits_from_pk(pk),
- pk->timestamp,
- pk->expiredate);
-
- if(pk->flags.revoked)
- fprintf(spawn->tochild,"r");
- if(pk->has_expired)
- fprintf(spawn->tochild,"e");
-
- fprintf(spawn->tochild,"\n");
- }
- break;
-
- case PKT_USER_ID:
- {
- PKT_user_id *uid=node->pkt->pkt.user_id;
- int r;
-
- if(uid->attrib_data)
- continue;
-
- fprintf(spawn->tochild,"uid:");
-
- /* Quote ':', '%', and any 8-bit
- characters */
- for(r=0;r<uid->len;r++)
- {
- if(uid->name[r]==':' || uid->name[r]=='%'
- || uid->name[r]&0x80)
- fprintf(spawn->tochild,"%%%02X",
- (byte)uid->name[r]);
- else
- fprintf(spawn->tochild,"%c",uid->name[r]);
- }
-
- fprintf(spawn->tochild,":%u:%u:",
- uid->created,uid->expiredate);
-
- if(uid->is_revoked)
- fprintf(spawn->tochild,"r");
- if(uid->is_expired)
- fprintf(spawn->tochild,"e");
-
- fprintf(spawn->tochild,"\n");
- }
- break;
-
- /* This bit is really for the benefit of
- people who store their keys in LDAP
- servers. It makes it easy to do queries
- for things like "all keys signed by
- Isabella". */
- case PKT_SIGNATURE:
- {
- PKT_signature *sig=node->pkt->pkt.signature;
-
- if(!IS_UID_SIG(sig))
- continue;
-
- fprintf(spawn->tochild,"sig:%08lX%08lX:%X:%u:%u\n",
- (ulong)sig->keyid[0],(ulong)sig->keyid[1],
- sig->sig_class,sig->timestamp,
- sig->expiredate);
- }
- break;
- }
- }
-
- fprintf(spawn->tochild,"INFO %08lX%08lX END\n",
- (ulong)block->pkt->pkt.public_key->keyid[0],
- (ulong)block->pkt->pkt.public_key->keyid[1]);
-
- fprintf(spawn->tochild,"KEY %08lX%08lX BEGIN\n",
- (ulong)block->pkt->pkt.public_key->keyid[0],
- (ulong)block->pkt->pkt.public_key->keyid[1]);
- fwrite(iobuf_get_temp_buffer(buffer),
- iobuf_get_temp_length(buffer),1,spawn->tochild);
- fprintf(spawn->tochild,"KEY %08lX%08lX END\n",
- (ulong)block->pkt->pkt.public_key->keyid[0],
- (ulong)block->pkt->pkt.public_key->keyid[1]);
-
- iobuf_close(buffer);
-
- if(keyserver->host)
- log_info(_("sending key %s to %s server %s\n"),
- keystr(block->pkt->pkt.public_key->keyid),
- keyserver->scheme,keyserver->host);
- else
- log_info(_("sending key %s to %s\n"),
- keystr(block->pkt->pkt.public_key->keyid),
- keyserver->uri);
-
- release_kbnode(block);
- }
-
- free_strlist(temp);
- }
-
- break;
- }
-
- case KS_SEARCH:
- {
- strlist_t key;
-
- fprintf(spawn->tochild,"COMMAND SEARCH\n\n");
-
- /* Which keys do we want? Remember that the gpgkeys_ program
- is going to lump these together into a search string. */
-
- for(key=list;key!=NULL;key=key->next)
- {
- fprintf(spawn->tochild,"%s\n",key->d);
- if(key!=list)
- {
- searchstr=xrealloc(searchstr,
- strlen(searchstr)+strlen(key->d)+2);
- strcat(searchstr," ");
- }
- else
- {
- searchstr=xmalloc(strlen(key->d)+1);
- searchstr[0]='\0';
- }
-
- strcat(searchstr,key->d);
- }
-
- fprintf(spawn->tochild,"\n");
-
- if(keyserver->host)
- log_info(_("searching for \"%s\" from %s server %s\n"),
- searchstr,keyserver->scheme,keyserver->host);
- else
- log_info(_("searching for \"%s\" from %s\n"),
- searchstr,keyserver->uri);
-
- break;
- }
+ char *str = line + 5;
+ char *tok;
- default:
- log_fatal(_("no keyserver action!\n"));
- break;
+ if ((tok = strsep (&str, ":")))
+ {
+ int version;
+
+ if (sscanf (tok, "%d", &version) !=1 )
+ version = 1;
+
+ if (version !=1 )
+ {
+ log_error (_("invalid keyserver protocol "
+ "(us %d!=handler %d)\n"), 1, version);
+ return gpg_error (GPG_ERR_UNSUPPORTED_PROTOCOL);
+ }
+ }
+
+ if ((tok = strsep (&str, ":"))
+ && sscanf (tok, "%d", &parm->count) == 1)
+ {
+ if (!parm->count)
+ parm->not_found = 1;/* Server indicated that no items follow. */
+ else if (parm->count < 0)
+ parm->count = 10; /* Bad value - assume something reasonable. */
+ else
+ parm->validcount = 1; /* COUNT seems to be okay. */
+ }
+
+ parm->any_lines = 1;
+ return 0; /* Line processing finished. */
}
- /* Done sending, so start reading. */
- ret=exec_read(spawn);
- if(ret)
- goto fail;
-
- /* Now handle the response */
-
- for(;;)
+ again:
+ if (line)
+ keyrec = parse_keyrec (line);
+ else
{
- int plen;
- char *ptr;
-
- maxlen=1024;
- if(iobuf_read_line(spawn->fromchild,&line,&buflen,&maxlen)==0)
- {
- ret = gpg_error_from_syserror ();
- goto fail; /* i.e. EOF */
- }
-
- ptr=line;
-
- /* remove trailing whitespace */
- plen=strlen(ptr);
- while(plen>0 && ascii_isspace(ptr[plen-1]))
- plen--;
- plen[ptr]='\0';
-
- if(*ptr=='\0')
- break;
-
- if(ascii_strncasecmp(ptr,"VERSION ",8)==0)
- {
- gotversion=1;
-
- if(atoi(&ptr[8])!=KEYSERVER_PROTO_VERSION)
- {
- log_error(_("invalid keyserver protocol (us %d!=handler %d)\n"),
- KEYSERVER_PROTO_VERSION,atoi(&ptr[8]));
- goto fail;
- }
- }
- else if(ascii_strncasecmp(ptr,"PROGRAM ",8)==0)
- {
- if(ascii_strncasecmp(&ptr[8],VERSION,strlen(VERSION))!=0)
- log_info(_("WARNING: keyserver handler from a different"
- " version of GnuPG (%s)\n"),&ptr[8]);
- }
- else if(ascii_strncasecmp(ptr,"OPTION OUTOFBAND",16)==0)
- outofband=1; /* Currently the only OPTION */
+ /* Received EOF - flush data */
+ parm->eof_seen = 1;
+ keyrec = parse_keyrec (NULL);
+ if (!keyrec)
+ {
+ if (!parm->nkeys)
+ parm->not_found = 1; /* No keys at all. */
+ else
+ {
+ if (parm->nkeys != parm->count)
+ parm->validcount = 0;
+
+ if (!(opt.with_colons && opt.batch))
+ {
+ err = show_prompt (parm->ctrl, parm->desc, parm->nkeys,
+ parm->validcount? parm->count : 0,
+ parm->searchstr_disp);
+ return err;
+ }
+ }
+ }
}
- if(!gotversion)
+ /* Save the key in the key array. */
+ if (keyrec)
{
- log_error(_("keyserver did not send VERSION\n"));
- goto fail;
+ /* Allocate or enlarge the key array if needed. */
+ if (!parm->desc)
+ {
+ if (parm->count < 1)
+ {
+ parm->count = 10;
+ parm->validcount = 0;
+ }
+ parm->desc = xtrymalloc (parm->count * sizeof *parm->desc);
+ if (!parm->desc)
+ {
+ err = gpg_error_from_syserror ();
+ iobuf_close (keyrec->uidbuf);
+ xfree (keyrec);
+ return err;
+ }
+ }
+ else if (parm->nkeys == parm->count)
+ {
+ /* Keyserver sent more keys than claimed in the info: line. */
+ KEYDB_SEARCH_DESC *tmp;
+ int newcount = parm->count + 10;
+
+ tmp = xtryrealloc (parm->desc, newcount * sizeof *parm->desc);
+ if (!tmp)
+ {
+ err = gpg_error_from_syserror ();
+ iobuf_close (keyrec->uidbuf);
+ xfree (keyrec);
+ return err;
+ }
+ parm->count = newcount;
+ parm->desc = tmp;
+ parm->validcount = 0;
+ }
+
+ parm->desc[parm->nkeys] = keyrec->desc;
+
+ if (!opt.with_colons)
+ {
+ /* SCREEN_LINES - 1 for the prompt. */
+ if (parm->numlines + keyrec->lines > opt.screen_lines - 1)
+ {
+ err = show_prompt (parm->ctrl, parm->desc, parm->nkeys,
+ parm->validcount ? parm->count:0,
+ parm->searchstr_disp);
+ if (err)
+ return err;
+ parm->numlines = 0;
+ }
+
+ print_keyrec (parm->nkeys+1, keyrec);
+ }
+
+ parm->numlines += keyrec->lines;
+ iobuf_close (keyrec->uidbuf);
+ xfree (keyrec);
+
+ parm->any_lines = 1;
+ parm->nkeys++;
+
+ /* If we are here due to a flush after the EOF, run again for
+ the last prompt. Fixme: Make this code better readable. */
+ if (parm->eof_seen)
+ goto again;
}
- if(!outofband)
- switch(action)
- {
- case KS_GET:
- case KS_GETNAME:
- {
- void *stats_handle;
-
- stats_handle=import_new_stats_handle();
-
- /* Slurp up all the key data. In the future, it might be
- nice to look for KEY foo OUTOFBAND and FAILED indicators.
- It's harmless to ignore them, but ignoring them does make
- gpg complain about "no valid OpenPGP data found". One
- way to do this could be to continue parsing this
- line-by-line and make a temp iobuf for each key. */
-
- /* FIXME: Pass CTRL. */
- import_keys_stream (NULL, spawn->fromchild,stats_handle,fpr,fpr_len,
- opt.keyserver_options.import_options);
-
- import_print_stats(stats_handle);
- import_release_stats_handle(stats_handle);
-
- break;
- }
-
- /* Nothing to do here */
- case KS_SEND:
- break;
-
- case KS_SEARCH:
- keyserver_search_prompt (ctrl, spawn->fromchild,searchstr);
- break;
-
- default:
- log_fatal(_("no keyserver action!\n"));
- break;
- }
-
- fail:
- xfree(line);
- xfree(searchstr);
-
-
- *prog=exec_finish(spawn);
-
- return ret;
+ return 0;
}
-static int
-keyserver_work (ctrl_t ctrl,
- enum ks_action action,strlist_t list,KEYDB_SEARCH_DESC *desc,
- int count,unsigned char **fpr,size_t *fpr_len,
- struct keyserver_spec *keyserver)
-{
- int rc=0,ret=0;
-
- if(!keyserver)
- {
- log_error(_("no keyserver known (use option --keyserver)\n"));
- return G10ERR_BAD_URI;
- }
-
-#ifdef DISABLE_KEYSERVER_HELPERS
-
- log_error(_("external keyserver calls are not supported in this build\n"));
- return G10ERR_KEYSERVER;
-
-#else
- /* Spawn a handler */
-
- rc = keyserver_spawn (ctrl, action, list, desc, count,
- &ret, fpr, fpr_len, keyserver);
- if(ret)
- {
- switch(ret)
- {
- case KEYSERVER_SCHEME_NOT_FOUND:
- log_error(_("no handler for keyserver scheme `%s'\n"),
- keyserver->scheme);
- break;
-
- case KEYSERVER_NOT_SUPPORTED:
- log_error(_("action `%s' not supported with keyserver "
- "scheme `%s'\n"),
- action==KS_GET?"get":action==KS_SEND?"send":
- action==KS_SEARCH?"search":"unknown",
- keyserver->scheme);
- break;
-
- case KEYSERVER_VERSION_ERROR:
- log_error(_(GPGKEYS_PREFIX "%s does not support"
- " handler version %d\n"),
- keyserver_typemap(keyserver->scheme),
- KEYSERVER_PROTO_VERSION);
- break;
-
- case KEYSERVER_TIMEOUT:
- log_error(_("keyserver timed out\n"));
- break;
-
- case KEYSERVER_INTERNAL_ERROR:
- default:
- log_error(_("keyserver internal error\n"));
- break;
- }
-
- return G10ERR_KEYSERVER;
- }
-
- if(rc)
- {
- log_error(_("keyserver communications error: %s\n"),g10_errstr(rc));
-
- return rc;
- }
- return 0;
-#endif /* ! DISABLE_KEYSERVER_HELPERS*/
-}
-int
+int
keyserver_export (ctrl_t ctrl, strlist_t users)
{
gpg_error_t err;
@@ -1628,14 +1014,14 @@ keyserver_export (ctrl_t ctrl, strlist_t users)
if(sl)
{
- rc = keyserver_work (ctrl, KS_SEND,sl,NULL,0,NULL,NULL,opt.keyserver);
+ rc = keyserver_put (ctrl, sl, opt.keyserver);
free_strlist(sl);
}
return rc;
}
-int
+int
keyserver_import (ctrl_t ctrl, strlist_t users)
{
gpg_error_t err;
@@ -1667,8 +1053,7 @@ keyserver_import (ctrl_t ctrl, strlist_t users)
}
if(count>0)
- rc=keyserver_work (ctrl, KS_GET, NULL, desc, count,
- NULL, NULL, opt.keyserver);
+ rc=keyserver_get (ctrl, desc, count, NULL);
xfree(desc);
@@ -1694,10 +1079,10 @@ keyserver_import_fprint (ctrl_t ctrl, const byte *fprint,size_t fprint_len,
/* TODO: Warn here if the fingerprint we got doesn't match the one
we asked for? */
- return keyserver_work (ctrl, KS_GET, NULL, &desc, 1, NULL, NULL, keyserver);
+ return keyserver_get (ctrl, &desc, 1, keyserver);
}
-int
+int
keyserver_import_keyid (ctrl_t ctrl,
u32 *keyid,struct keyserver_spec *keyserver)
{
@@ -1709,11 +1094,11 @@ keyserver_import_keyid (ctrl_t ctrl,
desc.u.kid[0]=keyid[0];
desc.u.kid[1]=keyid[1];
- return keyserver_work (ctrl, KS_GET,NULL,&desc,1,NULL,NULL,keyserver);
+ return keyserver_get (ctrl, &desc,1, keyserver);
}
/* code mostly stolen from do_export_stream */
-static int
+static int
keyidlist(strlist_t users,KEYDB_SEARCH_DESC **klist,int *count,int fakev3)
{
int rc=0,ndesc,num=100;
@@ -1736,13 +1121,13 @@ keyidlist(strlist_t users,KEYDB_SEARCH_DESC **klist,int *count,int fakev3)
}
else
{
- for (ndesc=0, sl=users; sl; sl = sl->next, ndesc++)
+ for (ndesc=0, sl=users; sl; sl = sl->next, ndesc++)
;
desc = xmalloc ( ndesc * sizeof *desc);
-
+
for (ndesc=0, sl=users; sl; sl = sl->next)
{
- gpg_error_t err;
+ gpg_error_t err;
if (!(err = classify_user_id (sl->d, desc+ndesc)))
ndesc++;
else
@@ -1753,7 +1138,7 @@ keyidlist(strlist_t users,KEYDB_SEARCH_DESC **klist,int *count,int fakev3)
while (!(rc = keydb_search (kdbhd, desc, ndesc)))
{
- if (!users)
+ if (!users)
desc[0].mode = KEYDB_SEARCH_MODE_NEXT;
/* read the keyblock */
@@ -1856,7 +1241,7 @@ keyidlist(strlist_t users,KEYDB_SEARCH_DESC **klist,int *count,int fakev3)
if(rc==-1)
rc=0;
-
+
leave:
if(rc)
xfree(*klist);
@@ -1913,9 +1298,7 @@ keyserver_refresh (ctrl_t ctrl, strlist_t users)
/* We use the keyserver structure we parsed out before.
Note that a preferred keyserver without a scheme://
will be interpreted as hkp:// */
-
- rc = keyserver_work (ctrl, KS_GET, NULL, &desc[i], 1,
- NULL, NULL, keyserver);
+ rc = keyserver_get (ctrl, &desc[i], 1, keyserver);
if(rc)
log_info(_("WARNING: unable to refresh key %s"
" via %s: %s\n"),keystr_from_desc(&desc[i]),
@@ -1945,8 +1328,7 @@ keyserver_refresh (ctrl_t ctrl, strlist_t users)
count,opt.keyserver->uri);
}
- rc=keyserver_work (ctrl, KS_GET, NULL, desc, numdesc,
- NULL, NULL, opt.keyserver);
+ rc=keyserver_get (ctrl, desc, numdesc, NULL);
}
xfree(desc);
@@ -1961,15 +1343,305 @@ keyserver_refresh (ctrl_t ctrl, strlist_t users)
return rc;
}
-int
+
+/* Search for keys on the keyservers. The patterns are given in the
+ string list TOKENS. */
+gpg_error_t
keyserver_search (ctrl_t ctrl, strlist_t tokens)
{
- if (tokens)
- return keyserver_work (ctrl, KS_SEARCH, tokens, NULL, 0,
- NULL, NULL, opt.keyserver);
- return 0;
+ gpg_error_t err;
+ char *searchstr;
+ struct search_line_handler_parm_s parm;
+
+ memset (&parm, 0, sizeof parm);
+
+ if (!tokens)
+ return 0; /* Return success if no patterns are given. */
+
+ if (!opt.keyserver)
+ {
+ log_error (_("no keyserver known (use option --keyserver)\n"));
+ return gpg_error (GPG_ERR_NO_KEYSERVER);
+ }
+
+ /* Write global options */
+
+ /* for(temp=opt.keyserver_options.other;temp;temp=temp->next) */
+ /* fprintf(spawn->tochild,"OPTION %s\n",temp->d); */
+
+ /* Write per-keyserver options */
+
+ /* for(temp=keyserver->options;temp;temp=temp->next) */
+ /* fprintf(spawn->tochild,"OPTION %s\n",temp->d); */
+
+ {
+ membuf_t mb;
+ strlist_t item;
+
+ init_membuf (&mb, 1024);
+ for (item = tokens; item; item = item->next)
+ {
+ if (item != tokens)
+ put_membuf (&mb, " ", 1);
+ put_membuf_str (&mb, item->d);
+ }
+ put_membuf (&mb, "", 1); /* Append Nul. */
+ searchstr = get_membuf (&mb, NULL);
+ if (!searchstr)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ }
+ /* FIXME: Enable the next line */
+ /* log_info (_("searching for \"%s\" from %s\n"), searchstr, keyserver->uri); */
+
+ parm.ctrl = ctrl;
+ if (searchstr)
+ parm.searchstr_disp = utf8_to_native (searchstr, strlen (searchstr), 0);
+
+ err = gpg_dirmngr_ks_search (ctrl, searchstr, search_line_handler, &parm);
+
+ if (parm.not_found)
+ {
+ if (parm.searchstr_disp)
+ log_info (_("key \"%s\" not found on keyserver\n"),
+ parm.searchstr_disp);
+ else
+ log_info (_("key not found on keyserver\n"));
+ }
+
+ if (gpg_err_code (err) == GPG_ERR_NO_KEYSERVER)
+ log_error (_("no keyserver known (use option --keyserver)\n"));
+ else if (err)
+ log_error ("error searching keyserver: %s\n", gpg_strerror (err));
+
+ /* switch(ret) */
+ /* { */
+ /* case KEYSERVER_SCHEME_NOT_FOUND: */
+ /* log_error(_("no handler for keyserver scheme `%s'\n"), */
+ /* opt.keyserver->scheme); */
+ /* break; */
+
+ /* case KEYSERVER_NOT_SUPPORTED: */
+ /* log_error(_("action `%s' not supported with keyserver " */
+ /* "scheme `%s'\n"), "search", opt.keyserver->scheme); */
+ /* break; */
+
+ /* case KEYSERVER_TIMEOUT: */
+ /* log_error(_("keyserver timed out\n")); */
+ /* break; */
+
+ /* case KEYSERVER_INTERNAL_ERROR: */
+ /* default: */
+ /* log_error(_("keyserver internal error\n")); */
+ /* break; */
+ /* } */
+
+ /* return gpg_error (GPG_ERR_KEYSERVER); */
+
+
+ leave:
+ xfree (parm.desc);
+ xfree (parm.searchstr_disp);
+ xfree(searchstr);
+
+ return err;
}
+
+
+/* Called using:
+
+import_name:
+ rc = keyserver_work (ctrl, KS_GETNAME, list, NULL,
+ 0, fpr, fpr_len, keyserver);
+
+import_ldap:
+ rc = keyserver_work (ctrl, KS_GETNAME, list, NULL,
+ 0, fpr, fpr_len, keyserver);
+
+ */
+
+static gpg_error_t
+keyserver_get (ctrl_t ctrl, KEYDB_SEARCH_DESC *desc, int ndesc,
+ struct keyserver_spec *keyserver)
+
+{
+ gpg_error_t err = 0;
+ char **pattern;
+ int idx, npat;
+ estream_t datastream;
+
+ /* Create an array filled with a search pattern for each key. The
+ array is delimited by a NULL entry. */
+ pattern = xtrycalloc (ndesc+1, sizeof *pattern);
+ if (!pattern)
+ return gpg_error_from_syserror ();
+ for (npat=idx=0; idx < ndesc; idx++)
+ {
+ int quiet = 0;
+
+ if (desc[idx].mode == KEYDB_SEARCH_MODE_FPR20
+ || desc[idx].mode == KEYDB_SEARCH_MODE_FPR16)
+ {
+ pattern[npat] = xtrymalloc (2+2*20+1);
+ if (!pattern[npat])
+ err = gpg_error_from_syserror ();
+ else
+ {
+ strcpy (pattern[npat], "0x");
+ bin2hex (desc[idx].u.fpr,
+ desc[idx].mode == KEYDB_SEARCH_MODE_FPR20? 20 : 16,
+ pattern[npat]+2);
+ npat++;
+ }
+ }
+ else if(desc[idx].mode == KEYDB_SEARCH_MODE_LONG_KID)
+ {
+ pattern[npat] = xtryasprintf ("0x%08lX%08lX",
+ (ulong)desc[idx].u.kid[0],
+ (ulong)desc[idx].u.kid[1]);
+ if (!pattern[npat])
+ err = gpg_error_from_syserror ();
+ else
+ npat++;
+ }
+ else if(desc[idx].mode == KEYDB_SEARCH_MODE_SHORT_KID)
+ {
+ pattern[npat] = xtryasprintf ("0x%08lX", (ulong)desc[idx].u.kid[1]);
+ if (!pattern[npat])
+ err = gpg_error_from_syserror ();
+ else
+ npat++;
+ }
+ else if(desc[idx].mode == KEYDB_SEARCH_MODE_EXACT)
+ {
+ /* FIXME: We don't need this. It is used as a dummy by
+ keyserver_fetch which passes an entire URL. Better use a
+ separate function here. */
+ pattern[npat] = xtrystrdup ("0x0000000000000000");
+ if (!pattern[npat])
+ err = gpg_error_from_syserror ();
+ else
+ {
+ npat++;
+ quiet = 1;
+ }
+ }
+ else if (desc[idx].mode == KEYDB_SEARCH_MODE_NONE)
+ continue;
+ else
+ BUG();
+
+ if (err)
+ {
+ for (idx=0; idx < npat; idx++)
+ xfree (pattern[idx]);
+ xfree (pattern);
+ return err;
+ }
+
+ if (!quiet && keyserver)
+ {
+ if (keyserver->host)
+ log_info (_("requesting key %s from %s server %s\n"),
+ keystr_from_desc (&desc[idx]),
+ keyserver->scheme, keyserver->host);
+ else
+ log_info (_("requesting key %s from %s\n"),
+ keystr_from_desc (&desc[idx]), keyserver->uri);
+ }
+ }
+
+
+ err = gpg_dirmngr_ks_get (ctrl, pattern, &datastream);
+ for (idx=0; idx < npat; idx++)
+ xfree (pattern[idx]);
+ xfree (pattern);
+ if (!err)
+ {
+ void *stats_handle;
+
+ stats_handle = import_new_stats_handle();
+
+ /* FIXME: Check whether this comment should be moved to dirmngr.
+
+ Slurp up all the key data. In the future, it might be nice
+ to look for KEY foo OUTOFBAND and FAILED indicators. It's
+ harmless to ignore them, but ignoring them does make gpg
+ complain about "no valid OpenPGP data found". One way to do
+ this could be to continue parsing this line-by-line and make
+ a temp iobuf for each key. */
+
+ import_keys_es_stream (ctrl, datastream, stats_handle, NULL, NULL,
+ opt.keyserver_options.import_options);
+
+ import_print_stats (stats_handle);
+ import_release_stats_handle (stats_handle);
+ }
+ es_fclose (datastream);
+
+
+ return err;
+}
+
+
+/* Send all keys specified by KEYSPECS to the KEYSERVERS. */
+static gpg_error_t
+keyserver_put (ctrl_t ctrl, strlist_t keyspecs,
+ struct keyserver_spec *keyserver)
+
+{
+ gpg_error_t err;
+ strlist_t kspec;
+
+ if (!keyspecs)
+ return 0; /* Return success if the list is empty. */
+
+ if (!opt.keyserver)
+ {
+ log_error (_("no keyserver known (use option --keyserver)\n"));
+ return gpg_error (GPG_ERR_NO_KEYSERVER);
+ }
+
+ for (kspec = keyspecs; kspec; kspec = kspec->next)
+ {
+ void *data;
+ size_t datalen;
+ kbnode_t keyblock;
+
+ err = export_pubkey_buffer (ctrl, kspec->d,
+ opt.keyserver_options.export_options,
+ &keyblock, &data, &datalen);
+ if (err)
+ log_error (_("skipped \"%s\": %s\n"), kspec->d, gpg_strerror (err));
+ else
+ {
+ if (keyserver->host)
+ log_info (_("sending key %s to %s server %s\n"),
+ keystr (keyblock->pkt->pkt.public_key->keyid),
+ keyserver->scheme, keyserver->host);
+ else
+ log_info (_("sending key %s to %s\n"),
+ keystr (keyblock->pkt->pkt.public_key->keyid),
+ keyserver->uri);
+
+ err = gpg_dirmngr_ks_put (ctrl, data, datalen, keyblock);
+ release_kbnode (keyblock);
+ xfree (data);
+ if (err)
+ log_error (_("keyserver send failed: %s\n"), gpg_strerror (err));
+ }
+ }
+
+
+ return err;
+
+}
+
+
+
int
keyserver_fetch (ctrl_t ctrl, strlist_t urilist)
{
@@ -1996,7 +1668,7 @@ keyserver_fetch (ctrl_t ctrl, strlist_t urilist)
{
int rc;
- rc = keyserver_work (ctrl, KS_GET, NULL, &desc, 1, NULL, NULL, spec);
+ rc = keyserver_get (ctrl, &desc, 1, spec);
if(rc)
log_info (_("WARNING: unable to fetch URI %s: %s\n"),
sl->d,g10_errstr(rc));
@@ -2139,8 +1811,9 @@ keyserver_import_name (ctrl_t ctrl, const char *name,
append_to_strlist(&list,name);
- rc = keyserver_work (ctrl, KS_GETNAME, list, NULL,
- 0, fpr, fpr_len, keyserver);
+ rc = gpg_error (GPG_ERR_NOT_IMPLEMENTED); /* FIXME */
+ /* keyserver_work (ctrl, KS_GETNAME, list, NULL, */
+ /* 0, fpr, fpr_len, keyserver); */
free_strlist(list);
@@ -2196,7 +1869,7 @@ keyserver_import_ldap (ctrl_t ctrl,
snprintf(port,7,":%u",srvlist[i].port);
strcat(keyserver->host,port);
}
-
+
strcat(keyserver->host," ");
}
@@ -2212,9 +1885,10 @@ keyserver_import_ldap (ctrl_t ctrl,
strcat(keyserver->host,domain);
append_to_strlist(&list,name);
-
- rc = keyserver_work (ctrl, KS_GETNAME, list, NULL,
- 0, fpr, fpr_len, keyserver);
+
+ rc = gpg_error (GPG_ERR_NOT_IMPLEMENTED); /*FIXME*/
+ /* keyserver_work (ctrl, KS_GETNAME, list, NULL, */
+ /* 0, fpr, fpr_len, keyserver); */
free_strlist(list);
diff --git a/g10/main.h b/g10/main.h
index c7980ac9a..1b6f30516 100644
--- a/g10/main.h
+++ b/g10/main.h
@@ -287,6 +287,9 @@ void import_keys (ctrl_t ctrl, char **fnames, int nnames,
int import_keys_stream (ctrl_t ctrl, iobuf_t inp, void *stats_hd,
unsigned char **fpr,
size_t *fpr_len, unsigned int options);
+int import_keys_es_stream (ctrl_t ctrl, estream_t fp, void *stats_handle,
+ unsigned char **fpr, size_t *fpr_len,
+ unsigned int options);
void *import_new_stats_handle (void);
void import_release_stats_handle (void *p);
void import_print_stats (void *hd);
@@ -299,6 +302,10 @@ int parse_export_options(char *str,unsigned int *options,int noisy);
int export_pubkeys (ctrl_t ctrl, strlist_t users, unsigned int options );
int export_pubkeys_stream (ctrl_t ctrl, iobuf_t out, strlist_t users,
kbnode_t *keyblock_out, unsigned int options );
+gpg_error_t export_pubkey_buffer (ctrl_t ctrl, const char *keyspec,
+ unsigned int options,
+ kbnode_t *r_keyblock,
+ void **r_data, size_t *r_datalen);
int export_seckeys (ctrl_t ctrl, strlist_t users);
int export_secsubkeys (ctrl_t ctrl, strlist_t users);
diff --git a/g10/options.h b/g10/options.h
index 28a2805a9..cd0140651 100644
--- a/g10/options.h
+++ b/g10/options.h
@@ -1,6 +1,6 @@
/* options.h
* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
- * 2007, 2010 Free Software Foundation, Inc.
+ * 2007, 2010, 2011 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
@@ -35,6 +35,13 @@
#endif
#endif
+/* Declaration of a keyserver spec type. The definition is found in
+ ../common/keyserver.h. */
+struct keyserver_spec;
+typedef struct keyserver_spec *keyserver_spec_t;
+
+
+/* Global options for GPG. */
EXTERN_UNLESS_MAIN_MODULE
struct
{
@@ -130,22 +137,7 @@ struct
int not_dash_escaped;
int escape_from;
int lock_once;
- struct keyserver_spec
- {
- char *uri;
- char *scheme;
- char *auth;
- char *host;
- char *port;
- char *path;
- char *opaque;
- strlist_t options;
- struct
- {
- unsigned int direct_uri:1;
- } flags;
- struct keyserver_spec *next;
- } *keyserver;
+ keyserver_spec_t keyserver; /* The list of configured keyservers. */
struct
{
unsigned int options;
@@ -245,7 +237,7 @@ struct
AKL_KEYSERVER,
AKL_SPEC
} type;
- struct keyserver_spec *spec;
+ keyserver_spec_t spec;
struct akl *next;
} *auto_key_locate;