diff options
Diffstat (limited to 'g10')
-rw-r--r-- | g10/ChangeLog | 24 | ||||
-rw-r--r-- | g10/Makefile.am | 1 | ||||
-rw-r--r-- | g10/call-dirmngr.c | 611 | ||||
-rw-r--r-- | g10/call-dirmngr.h | 32 | ||||
-rw-r--r-- | g10/export.c | 54 | ||||
-rw-r--r-- | g10/gpg.c | 45 | ||||
-rw-r--r-- | g10/gpg.h | 8 | ||||
-rw-r--r-- | g10/import.c | 26 | ||||
-rw-r--r-- | g10/keyserver-internal.h | 2 | ||||
-rw-r--r-- | g10/keyserver.c | 1438 | ||||
-rw-r--r-- | g10/main.h | 7 | ||||
-rw-r--r-- | g10/options.h | 28 |
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 ) { @@ -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: @@ -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; |