mirror of
git://git.gnupg.org/gnupg.git
synced 2025-01-03 12:11:33 +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;
|
||||
}
|
||||
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);
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
return out_of_core ();
|
||||
}
|
||||
snprintf (line, DIM(line)-1, "LOOKUP%s %s",
|
||||
cache_only? " --cache-only":"", pattern);
|
||||
line[DIM(line)-1] = 0;
|
||||
xfree (pattern);
|
||||
memset (&parm, 0, sizeof parm);
|
||||
init_membuf (&parm.saveddata, 1024);
|
||||
parm.data_cb = cb;
|
||||
parm.data_cb_value = cb_value;
|
||||
|
||||
parm.ctrl = ctrl;
|
||||
parm.ctx = ctx;
|
||||
parm.cb = cb;
|
||||
parm.cb_value = cb_value;
|
||||
parm.error = 0;
|
||||
init_membuf (&parm.data, 4096);
|
||||
err = assuan_transact (ctx, line, ks_search_data_cb, &parm,
|
||||
NULL, NULL, NULL, NULL);
|
||||
if (!err)
|
||||
err = cb (cb_value, NULL); /* Send EOF. */
|
||||
|
||||
rc = assuan_transact (ctx, line, lookup_cb, &parm,
|
||||
NULL, NULL, lookup_status_cb, &parm);
|
||||
xfree (get_membuf (&parm.data, &len));
|
||||
|
||||
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 )
|
||||
|
763
g10/keyserver.c
763
g10/keyserver.c
File diff suppressed because it is too large
Load Diff
@ -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…
x
Reference in New Issue
Block a user