mirror of
git://git.gnupg.org/gnupg.git
synced 2024-11-10 21:38:50 +01:00
Keyserver search and get basically works again.
This commit is contained in:
parent
c2c5d30db8
commit
357f8d5398
@ -35,7 +35,7 @@ endif
|
||||
if BUILD_GPG
|
||||
gpg = g10
|
||||
if !HAVE_W32CE_SYSTEM
|
||||
keyserver = keyserver
|
||||
keyserver =
|
||||
endif
|
||||
else
|
||||
gpg =
|
||||
|
@ -79,6 +79,7 @@ ks_action_search (ctrl_t ctrl, strlist_t patterns, estream_t outfp)
|
||||
{
|
||||
err = copy_stream (infp, outfp);
|
||||
es_fclose (infp);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -88,3 +89,62 @@ ks_action_search (ctrl_t ctrl, strlist_t patterns, estream_t outfp)
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
/* Get the requested keys (macthing PATTERNS) using all configured
|
||||
keyservers and write the result to the provided output stream. */
|
||||
gpg_error_t
|
||||
ks_action_get (ctrl_t ctrl, strlist_t patterns, estream_t outfp)
|
||||
{
|
||||
gpg_error_t err = 0;
|
||||
gpg_error_t first_err = 0;
|
||||
int any = 0;
|
||||
strlist_t sl;
|
||||
uri_item_t uri;
|
||||
estream_t infp;
|
||||
|
||||
if (!patterns)
|
||||
return gpg_error (GPG_ERR_NO_USER_ID);
|
||||
|
||||
/* FIXME: We only take care of the first keyserver. To fully
|
||||
support multiple keyservers we need to track the result for each
|
||||
pattern and use the next keyserver if one key was not found. The
|
||||
keyservers might not all be fully synced thus it is not clear
|
||||
whether the first keyserver has the freshest copy of the key.
|
||||
Need to think about a better strategy. */
|
||||
for (uri = ctrl->keyservers; !err && uri; uri = uri->next)
|
||||
{
|
||||
if (uri->parsed_uri->is_http)
|
||||
{
|
||||
any = 1;
|
||||
for (sl = patterns; !err && sl; sl = sl->next)
|
||||
{
|
||||
err = ks_hkp_get (ctrl, uri->parsed_uri, sl->d, &infp);
|
||||
if (err)
|
||||
{
|
||||
/* It is possible that a server does not carry a
|
||||
key, thus we only save the error and continue
|
||||
with the next pattern. FIXME: It is an open
|
||||
question how to return such an error condition to
|
||||
the caller. */
|
||||
first_err = err;
|
||||
err = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
err = copy_stream (infp, outfp);
|
||||
/* Reading from the keyserver should nver fail, thus
|
||||
return this error. */
|
||||
es_fclose (infp);
|
||||
infp = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!any)
|
||||
err = gpg_error (GPG_ERR_NO_KEYSERVER);
|
||||
else if (!err && first_err)
|
||||
err = first_err; /* fixme: Do we really want to do that? */
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -21,6 +21,7 @@
|
||||
#define DIRMNGR_KS_ACTION_H 1
|
||||
|
||||
gpg_error_t ks_action_search (ctrl_t ctrl, strlist_t patterns, estream_t outfp);
|
||||
gpg_error_t ks_action_get (ctrl_t ctrl, strlist_t patterns, estream_t outfp);
|
||||
|
||||
|
||||
#endif /*DIRMNGR_KS_ACTION_H*/
|
||||
|
@ -37,6 +37,117 @@
|
||||
#define MAX_REDIRECTS 2
|
||||
|
||||
|
||||
/* Send an HTTP request. On success returns an estream object at
|
||||
R_FP. HOSTPORTSTR is only used for diagnostics. */
|
||||
static gpg_error_t
|
||||
send_request (ctrl_t ctrl, const char *request, const char *hostportstr,
|
||||
estream_t *r_fp)
|
||||
{
|
||||
gpg_error_t err;
|
||||
http_t http = NULL;
|
||||
int redirects_left = MAX_REDIRECTS;
|
||||
estream_t fp = NULL;
|
||||
char *request_buffer = NULL;
|
||||
|
||||
*r_fp = NULL;
|
||||
once_more:
|
||||
err = http_open (&http, HTTP_REQ_GET, request,
|
||||
/* fixme: AUTH */ NULL,
|
||||
0,
|
||||
/* fixme: proxy*/ NULL,
|
||||
NULL, NULL,
|
||||
/*FIXME curl->srvtag*/NULL);
|
||||
if (!err)
|
||||
{
|
||||
fp = http_get_write_ptr (http);
|
||||
/* Avoid caches to get the most recent copy of the key. We set
|
||||
both the Pragma and Cache-Control versions of the header, so
|
||||
we're good with both HTTP 1.0 and 1.1. */
|
||||
es_fputs ("Pragma: no-cache\r\n"
|
||||
"Cache-Control: no-cache\r\n", fp);
|
||||
http_start_data (http);
|
||||
if (es_ferror (fp))
|
||||
err = gpg_error_from_syserror ();
|
||||
}
|
||||
if (err)
|
||||
{
|
||||
/* Fixme: After a redirection we show the old host name. */
|
||||
log_error (_("error connecting to `%s': %s\n"),
|
||||
hostportstr, gpg_strerror (err));
|
||||
goto leave;
|
||||
}
|
||||
|
||||
/* Wait for the response. */
|
||||
dirmngr_tick (ctrl);
|
||||
err = http_wait_response (http);
|
||||
if (err)
|
||||
{
|
||||
log_error (_("error reading HTTP response for `%s': %s\n"),
|
||||
hostportstr, gpg_strerror (err));
|
||||
goto leave;
|
||||
}
|
||||
|
||||
switch (http_get_status_code (http))
|
||||
{
|
||||
case 200:
|
||||
err = 0;
|
||||
break; /* Success. */
|
||||
|
||||
case 301:
|
||||
case 302:
|
||||
{
|
||||
const char *s = http_get_header (http, "Location");
|
||||
|
||||
log_info (_("URL `%s' redirected to `%s' (%u)\n"),
|
||||
request, s?s:"[none]", http_get_status_code (http));
|
||||
if (s && *s && redirects_left-- )
|
||||
{
|
||||
xfree (request_buffer);
|
||||
request_buffer = xtrystrdup (s);
|
||||
if (request_buffer)
|
||||
{
|
||||
request = request_buffer;
|
||||
http_close (http, 0);
|
||||
http = NULL;
|
||||
goto once_more;
|
||||
}
|
||||
err = gpg_error_from_syserror ();
|
||||
}
|
||||
else
|
||||
err = gpg_error (GPG_ERR_NO_DATA);
|
||||
log_error (_("too many redirections\n"));
|
||||
}
|
||||
goto leave;
|
||||
|
||||
default:
|
||||
log_error (_("error accessing `%s': http status %u\n"),
|
||||
request, http_get_status_code (http));
|
||||
err = gpg_error (GPG_ERR_NO_DATA);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
fp = http_get_read_ptr (http);
|
||||
if (!fp)
|
||||
{
|
||||
err = gpg_error (GPG_ERR_BUG);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
/* Return the read stream and close the HTTP context. */
|
||||
*r_fp = fp;
|
||||
fp = NULL;
|
||||
http_close (http, 1);
|
||||
http = NULL;
|
||||
|
||||
leave:
|
||||
es_fclose (fp);
|
||||
http_close (http, 0);
|
||||
xfree (request_buffer);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Search the keyserver identified by URI for keys matching PATTERN.
|
||||
On success R_FP has an open stream to read the data. */
|
||||
gpg_error_t
|
||||
@ -48,10 +159,8 @@ ks_hkp_search (ctrl_t ctrl, parsed_uri_t uri, const char *pattern,
|
||||
char fprbuf[2+40+1];
|
||||
const char *scheme;
|
||||
char portstr[10];
|
||||
http_t http = NULL;
|
||||
char *hostport = NULL;
|
||||
char *request = NULL;
|
||||
int redirects_left = MAX_REDIRECTS;
|
||||
estream_t fp = NULL;
|
||||
|
||||
*r_fp = NULL;
|
||||
@ -142,87 +251,11 @@ ks_hkp_search (ctrl_t ctrl, parsed_uri_t uri, const char *pattern,
|
||||
}
|
||||
|
||||
/* Send the request. */
|
||||
once_more:
|
||||
err = http_open (&http, HTTP_REQ_GET, request,
|
||||
/* fixme: AUTH */ NULL,
|
||||
0,
|
||||
/* fixme: proxy*/ NULL,
|
||||
NULL, NULL,
|
||||
/*FIXME curl->srvtag*/NULL);
|
||||
if (!err)
|
||||
{
|
||||
fp = http_get_write_ptr (http);
|
||||
/* Avoid caches to get the most recent copy of the key. We set
|
||||
both the Pragma and Cache-Control versions of the header, so
|
||||
we're good with both HTTP 1.0 and 1.1. */
|
||||
es_fputs ("Pragma: no-cache\r\n"
|
||||
"Cache-Control: no-cache\r\n", fp);
|
||||
http_start_data (http);
|
||||
if (es_ferror (fp))
|
||||
err = gpg_error_from_syserror ();
|
||||
}
|
||||
err = send_request (ctrl, request, hostport, &fp);
|
||||
if (err)
|
||||
{
|
||||
/* Fixme: After a redirection we show the old host name. */
|
||||
log_error (_("error connecting to `%s': %s\n"),
|
||||
hostport, gpg_strerror (err));
|
||||
goto leave;
|
||||
}
|
||||
|
||||
/* Wait for the response. */
|
||||
dirmngr_tick (ctrl);
|
||||
err = http_wait_response (http);
|
||||
if (err)
|
||||
{
|
||||
log_error (_("error reading HTTP response for `%s': %s\n"),
|
||||
hostport, gpg_strerror (err));
|
||||
goto leave;
|
||||
}
|
||||
|
||||
switch (http_get_status_code (http))
|
||||
{
|
||||
case 200:
|
||||
break; /* Success. */
|
||||
|
||||
case 301:
|
||||
case 302:
|
||||
{
|
||||
const char *s = http_get_header (http, "Location");
|
||||
|
||||
log_info (_("URL `%s' redirected to `%s' (%u)\n"),
|
||||
request, s?s:"[none]", http_get_status_code (http));
|
||||
if (s && *s && redirects_left-- )
|
||||
{
|
||||
xfree (request);
|
||||
request = xtrystrdup (s);
|
||||
if (request)
|
||||
{
|
||||
http_close (http, 0);
|
||||
http = NULL;
|
||||
goto once_more;
|
||||
}
|
||||
err = gpg_error_from_syserror ();
|
||||
}
|
||||
else
|
||||
err = gpg_error (GPG_ERR_NO_DATA);
|
||||
log_error (_("too many redirections\n"));
|
||||
}
|
||||
goto leave;
|
||||
|
||||
default:
|
||||
log_error (_("error accessing `%s': http status %u\n"),
|
||||
request, http_get_status_code (http));
|
||||
err = gpg_error (GPG_ERR_NO_DATA);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
/* Start reading the response. */
|
||||
fp = http_get_read_ptr (http);
|
||||
if (!fp)
|
||||
{
|
||||
err = gpg_error (GPG_ERR_BUG);
|
||||
goto leave;
|
||||
}
|
||||
{
|
||||
int c = es_getc (fp);
|
||||
if (c == -1)
|
||||
@ -241,15 +274,110 @@ ks_hkp_search (ctrl_t ctrl, parsed_uri_t uri, const char *pattern,
|
||||
es_ungetc (c, fp);
|
||||
}
|
||||
|
||||
/* Return the read stream and close the HTTP context. */
|
||||
/* Return the read stream. */
|
||||
*r_fp = fp;
|
||||
fp = NULL;
|
||||
|
||||
leave:
|
||||
es_fclose (fp);
|
||||
xfree (request);
|
||||
xfree (hostport);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
/* Get the key described key the KEYSPEC string from the keyserver
|
||||
identified by URI. On success R_FP has an open stream to read the
|
||||
data. */
|
||||
gpg_error_t
|
||||
ks_hkp_get (ctrl_t ctrl, parsed_uri_t uri, const char *keyspec, estream_t *r_fp)
|
||||
{
|
||||
gpg_error_t err;
|
||||
KEYDB_SEARCH_DESC desc;
|
||||
char kidbuf[8+1];
|
||||
const char *scheme;
|
||||
char portstr[10];
|
||||
char *hostport = NULL;
|
||||
char *request = NULL;
|
||||
estream_t fp = NULL;
|
||||
|
||||
*r_fp = NULL;
|
||||
|
||||
/* Remove search type indicator and adjust PATTERN accordingly.
|
||||
Note that HKP keyservers like the 0x to be present when searching
|
||||
by keyid. We need to re-format the fingerprint and keyids so to
|
||||
remove the gpg specific force-use-of-this-key flag ("!"). */
|
||||
err = classify_user_id (keyspec, &desc);
|
||||
if (err)
|
||||
return err;
|
||||
switch (desc.mode)
|
||||
{
|
||||
case KEYDB_SEARCH_MODE_SHORT_KID:
|
||||
case KEYDB_SEARCH_MODE_LONG_KID:
|
||||
snprintf (kidbuf, sizeof kidbuf, "%08lX", (ulong)desc.u.kid[1]);
|
||||
break;
|
||||
case KEYDB_SEARCH_MODE_FPR20:
|
||||
case KEYDB_SEARCH_MODE_FPR:
|
||||
/* This is a v4 fingerprint. Take the last 8 hex digits from
|
||||
the fingerprint which is the expected short keyid. */
|
||||
bin2hex (desc.u.fpr+16, 4, kidbuf);
|
||||
break;
|
||||
|
||||
case KEYDB_SEARCH_MODE_FPR16:
|
||||
log_error ("HKP keyserver do not support v3 fingerprints\n");
|
||||
default:
|
||||
return gpg_error (GPG_ERR_INV_USER_ID);
|
||||
}
|
||||
|
||||
/* Map scheme and port. */
|
||||
if (!strcmp (uri->scheme,"hkps") || !strcmp (uri->scheme,"https"))
|
||||
{
|
||||
scheme = "https";
|
||||
strcpy (portstr, "443");
|
||||
}
|
||||
else /* HKP or HTTP. */
|
||||
{
|
||||
scheme = "http";
|
||||
strcpy (portstr, "11371");
|
||||
}
|
||||
if (uri->port)
|
||||
snprintf (portstr, sizeof portstr, "%hu", uri->port);
|
||||
else
|
||||
{} /*fixme_do_srv_lookup ()*/
|
||||
|
||||
/* Build the request string. */
|
||||
{
|
||||
hostport = strconcat (scheme, "://",
|
||||
*uri->host? uri->host: "localhost",
|
||||
":", portstr, NULL);
|
||||
if (!hostport)
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
goto leave;
|
||||
}
|
||||
|
||||
request = strconcat (hostport,
|
||||
"/pks/lookup?op=get&options=mr&search=0x",
|
||||
kidbuf,
|
||||
NULL);
|
||||
if (!request)
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
goto leave;
|
||||
}
|
||||
}
|
||||
|
||||
/* Send the request. */
|
||||
err = send_request (ctrl, request, hostport, &fp);
|
||||
if (err)
|
||||
goto leave;
|
||||
|
||||
/* Return the read stream and close the HTTP context. */
|
||||
*r_fp = fp;
|
||||
fp = NULL;
|
||||
http_close (http, 1);
|
||||
http = NULL;
|
||||
|
||||
leave:
|
||||
es_fclose (fp);
|
||||
http_close (http, 0);
|
||||
xfree (request);
|
||||
xfree (hostport);
|
||||
return err;
|
||||
|
@ -26,6 +26,8 @@
|
||||
/*-- ks-engine-hkp.c --*/
|
||||
gpg_error_t ks_hkp_search (ctrl_t ctrl, parsed_uri_t uri, const char *pattern,
|
||||
estream_t *r_fp);
|
||||
gpg_error_t ks_hkp_get (ctrl_t ctrl, parsed_uri_t uri,
|
||||
const char *keyspec, estream_t *r_fp);
|
||||
|
||||
|
||||
|
||||
|
@ -1475,6 +1475,66 @@ cmd_ks_search (assuan_context_t ctx, char *line)
|
||||
}
|
||||
|
||||
|
||||
|
||||
static const char hlp_ks_get[] =
|
||||
"KS_GET {<pattern>}\n"
|
||||
"\n"
|
||||
"Get the keys matching PATTERN from the configured OpenPGP keyservers\n"
|
||||
"(see command KEYSERVER). Each pattern should be a keyid or a fingerprint";
|
||||
static gpg_error_t
|
||||
cmd_ks_get (assuan_context_t ctx, char *line)
|
||||
{
|
||||
ctrl_t ctrl = assuan_get_pointer (ctx);
|
||||
gpg_error_t err;
|
||||
strlist_t list, sl;
|
||||
char *p;
|
||||
estream_t outfp;
|
||||
|
||||
/* No options for now. */
|
||||
line = skip_options (line);
|
||||
|
||||
/* Break the line down into an strlist. Each pattern is by
|
||||
definition percent-plus escaped. However we only support keyids
|
||||
and fingerprints and thus the client has no need to apply the
|
||||
escaping. */
|
||||
list = NULL;
|
||||
for (p=line; *p; line = p)
|
||||
{
|
||||
while (*p && *p != ' ')
|
||||
p++;
|
||||
if (*p)
|
||||
*p++ = 0;
|
||||
if (*line)
|
||||
{
|
||||
sl = xtrymalloc (sizeof *sl + strlen (line));
|
||||
if (!sl)
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
free_strlist (list);
|
||||
goto leave;
|
||||
}
|
||||
sl->flags = 0;
|
||||
strcpy_escaped_plus (sl->d, line);
|
||||
sl->next = list;
|
||||
list = sl;
|
||||
}
|
||||
}
|
||||
|
||||
/* Setup an output stream and perform the get. */
|
||||
outfp = es_fopencookie (ctx, "w", data_line_cookie_functions);
|
||||
if (!outfp)
|
||||
err = set_error (GPG_ERR_ASS_GENERAL, "error setting up a data stream");
|
||||
else
|
||||
{
|
||||
err = ks_action_get (ctrl, list, outfp);
|
||||
es_fclose (outfp);
|
||||
}
|
||||
|
||||
leave:
|
||||
return leave_cmd (ctx, err);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
static const char hlp_getinfo[] =
|
||||
@ -1611,6 +1671,7 @@ register_commands (assuan_context_t ctx)
|
||||
{ "VALIDATE", cmd_validate, hlp_validate },
|
||||
{ "KEYSERVER", cmd_keyserver, hlp_keyserver },
|
||||
{ "KS_SEARCH", cmd_ks_search, hlp_ks_search },
|
||||
{ "KS_GET", cmd_ks_get, hlp_ks_get },
|
||||
{ "GETINFO", cmd_getinfo, hlp_getinfo },
|
||||
{ "KILLDIRMNGR",cmd_killdirmngr,hlp_killdirmngr },
|
||||
{ "RELOADDIRMNGR",cmd_reloaddirmngr,hlp_reloaddirmngr },
|
||||
|
@ -1,3 +1,11 @@
|
||||
2011-01-18 Werner Koch <wk@g10code.com>
|
||||
|
||||
* import.c (import_keys_es_stream): New.
|
||||
|
||||
2011-01-14 Werner Koch <wk@g10code.com>
|
||||
|
||||
* keyserver.c (parse_keyrec): Use trim_trailing_ws.
|
||||
|
||||
2011-01-07 Werner Koch <wk@g10code.com>
|
||||
|
||||
* call-dirmngr.c, call-dirmngr.h: New.
|
||||
|
@ -36,9 +36,29 @@
|
||||
#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;
|
||||
};
|
||||
|
||||
|
||||
/* 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
|
||||
@ -53,7 +73,7 @@ struct dirmngr_local_s
|
||||
struct dirmngr_local_s *next;
|
||||
|
||||
/* The active Assuan context. */
|
||||
static assuan_context_t ctx;
|
||||
assuan_context_t ctx;
|
||||
|
||||
/* Flag set to true while an operation is running on CTX. */
|
||||
int is_active;
|
||||
@ -106,12 +126,12 @@ create_context (ctrl_t ctrl, assuan_context_t *r_ctx)
|
||||
/* Set all configured keyservers. We clear existing keyservers
|
||||
so that any keyserver configured in GPG overrides keyservers
|
||||
possibly configured in Dirmngr. */
|
||||
if (ksi = opt.keyservers; !err && ksi; ksi = ksi->next)
|
||||
for (ksi = opt.keyserver; !err && ksi; ksi = ksi->next)
|
||||
{
|
||||
char *line;
|
||||
|
||||
line = xtryasprintf ("KEYSERVER%s %s",
|
||||
ksi == opt.keyservers? " --clear":"", ksi->uri);
|
||||
ksi == opt.keyserver? " --clear":"", ksi->uri);
|
||||
if (!line)
|
||||
err = gpg_error_from_syserror ();
|
||||
else
|
||||
@ -156,7 +176,8 @@ open_context (ctrl_t ctrl, assuan_context_t *r_ctx)
|
||||
/* Found an inactive local session - return that. */
|
||||
assert (!dml->is_active);
|
||||
dml->is_active = 1;
|
||||
return dml;
|
||||
*r_ctx = dml->ctx;
|
||||
return 0;
|
||||
}
|
||||
|
||||
dml = xtrycalloc (1, sizeof *dml);
|
||||
@ -192,9 +213,9 @@ close_context (ctrl_t ctrl, assuan_context_t ctx)
|
||||
{
|
||||
if (dml->ctx == ctx)
|
||||
{
|
||||
if (!ctx->is_active)
|
||||
if (!dml->is_active)
|
||||
log_fatal ("closing inactive dirmngr context %p\n", ctx);
|
||||
ctx->is_active = 0;
|
||||
dml->is_active = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -203,54 +224,215 @@ close_context (ctrl_t ctrl, assuan_context_t 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];
|
||||
|
||||
int
|
||||
gpg_dirmngr_ks_search (ctrl_t ctrl, strlist_t names,
|
||||
void (*cb)(void*, ksba_cert_t), void *cb_value)
|
||||
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;
|
||||
char *pattern;
|
||||
struct ks_search_parm_s parm;
|
||||
char line[ASSUAN_LINELENGTH];
|
||||
|
||||
err = open_context (ctrl, &ctx);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
pattern = pattern_from_strlist (names);
|
||||
if (!pattern)
|
||||
{
|
||||
if (ctx == dirmngr_ctx)
|
||||
release_dirmngr (ctrl);
|
||||
else
|
||||
release_dirmngr2 (ctrl);
|
||||
|
||||
return out_of_core ();
|
||||
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);
|
||||
}
|
||||
snprintf (line, DIM(line)-1, "LOOKUP%s %s",
|
||||
cache_only? " --cache-only":"", pattern);
|
||||
line[DIM(line)-1] = 0;
|
||||
xfree (pattern);
|
||||
|
||||
parm.ctrl = ctrl;
|
||||
parm.ctx = ctx;
|
||||
parm.cb = cb;
|
||||
parm.cb_value = cb_value;
|
||||
parm.error = 0;
|
||||
init_membuf (&parm.data, 4096);
|
||||
memset (&parm, 0, sizeof parm);
|
||||
init_membuf (&parm.saveddata, 1024);
|
||||
parm.data_cb = cb;
|
||||
parm.data_cb_value = cb_value;
|
||||
|
||||
rc = assuan_transact (ctx, line, lookup_cb, &parm,
|
||||
NULL, NULL, lookup_status_cb, &parm);
|
||||
xfree (get_membuf (&parm.data, &len));
|
||||
err = assuan_transact (ctx, line, ks_search_data_cb, &parm,
|
||||
NULL, NULL, NULL, NULL);
|
||||
if (!err)
|
||||
err = cb (cb_value, NULL); /* Send EOF. */
|
||||
|
||||
if (ctx == dirmngr_ctx)
|
||||
release_dirmngr (ctrl);
|
||||
else
|
||||
release_dirmngr2 (ctrl);
|
||||
|
||||
if (rc)
|
||||
return rc;
|
||||
xfree (get_membuf (&parm.saveddata, NULL));
|
||||
xfree (parm.helpbuf);
|
||||
|
||||
close_context (ctrl, ctx);
|
||||
return parm.error;
|
||||
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;
|
||||
}
|
||||
|
@ -21,6 +21,10 @@
|
||||
|
||||
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);
|
||||
|
||||
|
||||
#endif /*GNUPG_G10_CALL_DIRMNGR_H*/
|
||||
|
10
g10/gpg.c
10
g10/gpg.c
@ -3752,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:
|
||||
|
26
g10/import.c
26
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 )
|
||||
|
735
g10/keyserver.c
735
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>
|
||||
@ -46,7 +49,7 @@
|
||||
#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.
|
||||
@ -66,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[]=
|
||||
@ -94,6 +115,10 @@ 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);
|
||||
|
||||
|
||||
/* Reasonable guess */
|
||||
#define DEFAULT_MAX_CERT_SIZE 16384
|
||||
@ -556,6 +581,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;
|
||||
@ -584,12 +611,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;
|
||||
@ -728,220 +750,244 @@ 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",
|
||||
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')
|
||||
if (answer[0]=='q' || answer[0]=='Q')
|
||||
err = gpg_error (GPG_ERR_CANCELED);
|
||||
else if (atoi (answer) >= 1 && atoi (answer) <= numdesc)
|
||||
{
|
||||
xfree(answer);
|
||||
return 1;
|
||||
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;
|
||||
}
|
||||
else if(atoi(answer)>=1 && atoi(answer)<=numdesc)
|
||||
numarray[numidx++] = atoi (num);
|
||||
}
|
||||
|
||||
if (!numidx)
|
||||
goto again;
|
||||
|
||||
{
|
||||
char *split=answer,*num;
|
||||
KEYDB_SEARCH_DESC *selarray;
|
||||
|
||||
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);
|
||||
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(answer);
|
||||
return 1;
|
||||
leave:
|
||||
xfree (answer);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
/* 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)
|
||||
{
|
||||
struct search_line_handler_parm_s *parm = opaque;
|
||||
gpg_error_t err = 0;
|
||||
struct keyrec *keyrec;
|
||||
|
||||
if (parm->eof_seen && line)
|
||||
{
|
||||
log_debug ("ooops: unexpected data after EOF\n");
|
||||
line = NULL;
|
||||
}
|
||||
|
||||
/* Print the received line. */
|
||||
if (opt.with_colons && line)
|
||||
{
|
||||
log_debug ("%s\n",line);
|
||||
}
|
||||
|
||||
/* 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))
|
||||
{
|
||||
char *str = line + 5;
|
||||
char *tok;
|
||||
|
||||
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. */
|
||||
}
|
||||
|
||||
again:
|
||||
if (line)
|
||||
keyrec = parse_keyrec (line);
|
||||
else
|
||||
{
|
||||
/* 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Save the key in the key array. */
|
||||
if (keyrec)
|
||||
{
|
||||
/* 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;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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"));
|
||||
}
|
||||
|
||||
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
|
||||
@ -1513,7 +1559,7 @@ keyserver_spawn (ctrl_t ctrl,
|
||||
break;
|
||||
|
||||
case KS_SEARCH:
|
||||
keyserver_search_prompt (ctrl, spawn->fromchild,searchstr);
|
||||
//keyserver_search_prompt (ctrl, spawn->fromchild,searchstr);
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -1532,6 +1578,8 @@ keyserver_spawn (ctrl_t ctrl,
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
static int
|
||||
keyserver_work (ctrl_t ctrl,
|
||||
enum ks_action action,strlist_t list,KEYDB_SEARCH_DESC *desc,
|
||||
@ -1596,6 +1644,10 @@ keyserver_work (ctrl_t ctrl,
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int
|
||||
keyserver_export (ctrl_t ctrl, strlist_t users)
|
||||
{
|
||||
@ -1661,8 +1713,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);
|
||||
|
||||
@ -1688,7 +1739,7 @@ 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
|
||||
@ -1703,7 +1754,7 @@ 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 */
|
||||
@ -1907,9 +1958,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]),
|
||||
@ -1939,8 +1988,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);
|
||||
@ -1955,16 +2003,18 @@ keyserver_refresh (ctrl_t ctrl, strlist_t users)
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/* 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)
|
||||
{
|
||||
gpg_error_t err;
|
||||
int rc=0,ret=0;
|
||||
char *searchstr;
|
||||
struct search_line_handler_parm_s parm;
|
||||
|
||||
memset (&parm, 0, sizeof parm);
|
||||
|
||||
/* FIXME: WORK IN PROGRESS */
|
||||
if (!tokens)
|
||||
return 0; /* Return success if no patterns are given. */
|
||||
|
||||
@ -1974,6 +2024,58 @@ keyserver_search (ctrl_t ctrl, strlist_t tokens)
|
||||
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: */
|
||||
@ -1999,56 +2101,175 @@ keyserver_search (ctrl_t ctrl, strlist_t tokens)
|
||||
/* return gpg_error (GPG_ERR_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); */
|
||||
|
||||
/* Which keys do we want? Remember that the gpgkeys_ program
|
||||
is going to lump these together into a search string. */
|
||||
{
|
||||
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 ();
|
||||
}
|
||||
}
|
||||
log_info (_("searching for \"%s\" from %s\n"), searchstr, keyserver->uri);
|
||||
|
||||
{
|
||||
estream_t fp;
|
||||
err = gpg_dirmngr_ks_search (ctrl, searchstr, &fp);
|
||||
|
||||
keyserver_search_prompt (ctrl, fp,searchstr);
|
||||
}
|
||||
|
||||
leave:
|
||||
xfree(line);
|
||||
xfree (parm.desc);
|
||||
xfree (parm.searchstr_disp);
|
||||
xfree(searchstr);
|
||||
|
||||
|
||||
*prog=exec_finish(spawn);
|
||||
|
||||
return ret;
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Called using:
|
||||
|
||||
show_prompt:
|
||||
import:
|
||||
import_foo:
|
||||
refresh:
|
||||
rc=keyserver_work (ctrl, KS_GET, NULL, desc, count,
|
||||
NULL, NULL, opt.keyserver);
|
||||
|
||||
|
||||
fetch:
|
||||
rc = keyserver_work (ctrl, KS_GET, NULL, &desc, 1, NULL, NULL, spec);
|
||||
if(rc)
|
||||
log_info (_("WARNING: unable to fetch URI %s: %s\n"),
|
||||
sl->d,g10_errstr(rc));
|
||||
|
||||
|
||||
export:
|
||||
rc = keyserver_work (ctrl, KS_SEND,sl,NULL,0,NULL,NULL,opt.keyserver);
|
||||
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int
|
||||
keyserver_fetch (ctrl_t ctrl, strlist_t urilist)
|
||||
{
|
||||
@ -2075,7 +2296,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));
|
||||
|
@ -272,6 +272,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);
|
||||
|
Loading…
Reference in New Issue
Block a user