diff --git a/common/ChangeLog b/common/ChangeLog index 3ce80cb1a..3b7506492 100644 --- a/common/ChangeLog +++ b/common/ChangeLog @@ -1,3 +1,14 @@ +2011-01-20 Werner Koch + + * util.h (struct b64state): Add field LASTERR. + * b64enc.c (enc_start, b64enc_write, b64enc_finish): Handle + LASTERR. This is to make sure that we don't leak strduped data. + * b64dec.c (b64dec_start, b64dec_proc, b64dec_finish): Ditto. + + * http.c (escape_data): New. + (insert_escapes): Implement using escape_data. + (http_escape_data): New. + 2011-01-18 Werner Koch * iobuf.c (file_es_filter_ctx_t): New. diff --git a/common/b64dec.c b/common/b64dec.c index af223aef2..137dd7216 100644 --- a/common/b64dec.c +++ b/common/b64dec.c @@ -1,5 +1,5 @@ /* b64dec.c - Simple Base64 decoder. - * Copyright (C) 2008 Free Software Foundation, Inc. + * Copyright (C) 2008, 2011 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -72,16 +72,19 @@ b64dec_start (struct b64state *state, const char *title) if (title) { if (!strncmp (title, "PGP", 3) && (!title[3] || title[3] == ' ')) - return gpg_error (GPG_ERR_NOT_IMPLEMENTED); - - state->title = xtrystrdup (title); - if (!state->title) - return gpg_error_from_syserror (); - state->idx = s_init; + state->lasterr = gpg_error (GPG_ERR_NOT_IMPLEMENTED); + else + { + state->title = xtrystrdup (title); + if (!state->title) + state->lasterr = gpg_error_from_syserror (); + else + state->idx = s_init; + } } else state->idx = s_b64_0; - return 0; + return state->lasterr; } @@ -96,12 +99,18 @@ b64dec_proc (struct b64state *state, void *buffer, size_t length, int pos = state->quad_count; char *d, *s; + if (state->lasterr) + return state->lasterr; + if (state->stop_seen) { *r_nbytes = 0; - return gpg_error (GPG_ERR_EOF); + state->lasterr = gpg_error (GPG_ERR_EOF); + xfree (state->title); + state->title = NULL; + return state->lasterr; } - + for (s=d=buffer; length && !state->stop_seen; length--, s++) { switch (ds) @@ -210,6 +219,9 @@ b64dec_proc (struct b64state *state, void *buffer, size_t length, gpg_error_t b64dec_finish (struct b64state *state) { + if (state->lasterr) + return state->lasterr; + xfree (state->title); state->title = NULL; return state->invalid_encoding? gpg_error(GPG_ERR_BAD_DATA): 0; diff --git a/common/b64enc.c b/common/b64enc.c index 1e277f4cb..5d616198e 100644 --- a/common/b64enc.c +++ b/common/b64enc.c @@ -1,5 +1,6 @@ /* b64enc.c - Simple Base64 encoder. - * Copyright (C) 2001, 2003, 2004, 2008, 2010 Free Software Foundation, Inc. + * Copyright (C) 2001, 2003, 2004, 2008, 2010, + * 2011 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -143,6 +144,7 @@ enc_start (struct b64state *state, FILE *fp, estream_t stream, memset (state, 0, sizeof *state); state->fp = fp; state->stream = stream; + state->lasterr = 0; if (title && !*title) state->flags |= B64ENC_NO_LINEFEEDS; else if (title) @@ -154,9 +156,9 @@ enc_start (struct b64state *state, FILE *fp, estream_t stream, } state->title = xtrystrdup (title); if (!state->title) - return gpg_error_from_syserror (); + state->lasterr = gpg_error_from_syserror (); } - return 0; + return state->lasterr; } @@ -203,6 +205,8 @@ b64enc_write (struct b64state *state, const void *buffer, size_t nbytes) int idx, quad_count; const unsigned char *p; + if (state->lasterr) + return state->lasterr; if (!nbytes) { @@ -285,7 +289,13 @@ b64enc_write (struct b64state *state, const void *buffer, size_t nbytes) return 0; write_error: - return gpg_error_from_syserror (); + state->lasterr = gpg_error_from_syserror (); + if (state->title) + { + xfree (state->title); + state->title = NULL; + } + return state->lasterr; } @@ -297,6 +307,9 @@ b64enc_finish (struct b64state *state) int idx, quad_count; char tmp[4]; + if (state->lasterr) + return state->lasterr; + if (!(state->flags & B64ENC_DID_HEADER)) goto cleanup; @@ -404,6 +417,7 @@ b64enc_finish (struct b64state *state) } state->fp = NULL; state->stream = NULL; + state->lasterr = err; return err; } diff --git a/common/http.c b/common/http.c index 3d7c463b5..4d3536114 100644 --- a/common/http.c +++ b/common/http.c @@ -1,6 +1,6 @@ /* http.c - HTTP protocol handler - * Copyright (C) 1999, 2001, 2002, 2003, 2004, 2006, - * 2009, 2010 Free Software Foundation, Inc. + * Copyright (C) 1999, 2001, 2002, 2003, 2004, 2006, 2009, 2010, + * 2011 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -742,14 +742,14 @@ remove_escapes (char *string) } -static int -insert_escapes (char *buffer, const char *string, - const char *special) +static size_t +escape_data (char *buffer, const void *data, size_t datalen, + const char *special) { - const unsigned char *s = (const unsigned char*)string; - int n = 0; + const unsigned char *s; + size_t n = 0; - for (; *s; s++) + for (s = data; datalen; s++, datalen--) { if (strchr (VALID_URI_CHARS, *s) && !strchr (special, *s)) { @@ -771,6 +771,14 @@ insert_escapes (char *buffer, const char *string, } +static int +insert_escapes (char *buffer, const char *string, + const char *special) +{ + return escape_data (buffer, string, strlen (string), special); +} + + /* Allocate a new string from STRING using standard HTTP escaping as well as escaping of characters given in SPECIALS. A common pattern for SPECIALS is "%;?&=". However it depends on the needs, for @@ -792,6 +800,27 @@ http_escape_string (const char *string, const char *specials) return buf; } +/* Allocate a new string from {DATA,DATALEN} using standard HTTP + escaping as well as escaping of characters given in SPECIALS. A + common pattern for SPECIALS is "%;?&=". However it depends on the + needs, for example "+" and "/: often needs to be escaped too. + Returns NULL on failure and sets ERRNO. */ +char * +http_escape_data (const void *data, size_t datalen, const char *specials) +{ + int n; + char *buf; + + n = escape_data (NULL, data, datalen, specials); + buf = xtrymalloc (n+1); + if (buf) + { + escape_data (buf, data, datalen, specials); + buf[n] = 0; + } + return buf; +} + static uri_tuple_t diff --git a/common/http.h b/common/http.h index aaa2d3a13..7eecbc004 100644 --- a/common/http.h +++ b/common/http.h @@ -117,6 +117,7 @@ unsigned int http_get_status_code (http_t hd); const char *http_get_header (http_t hd, const char *name); char *http_escape_string (const char *string, const char *specials); +char *http_escape_data (const void *data, size_t datalen, const char *specials); #endif /*GNUPG_COMMON_HTTP_H*/ diff --git a/common/util.h b/common/util.h index 1f7964fc4..f06701fc0 100644 --- a/common/util.h +++ b/common/util.h @@ -150,6 +150,7 @@ struct b64state u32 crc; int stop_seen:1; int invalid_encoding:1; + gpg_error_t lasterr; }; gpg_error_t b64enc_start (struct b64state *state, FILE *fp, const char *title); diff --git a/dirmngr/ChangeLog b/dirmngr/ChangeLog index f5b3dea12..47c7fe012 100644 --- a/dirmngr/ChangeLog +++ b/dirmngr/ChangeLog @@ -1,13 +1,9 @@ -2011-01-06 Werner Koch +2011-01-20 Werner Koch * server.c (release_ctrl_keyservers): New. - (cmd_keyserver): New. - + (cmd_keyserver, cmd_ks_seach, cmd_ks_get, cmd_ks_put): New. * dirmngr.h (uri_item_t): New. (struct server_control_s): Add field KEYSERVERS. - -2011-01-04 Werner Koch - * ks-engine-hkp.c: New. * ks-engine.h: New. * ks-action.c, ks-action.h: New. diff --git a/dirmngr/ks-action.c b/dirmngr/ks-action.c index f376c27d1..fd2a2b568 100644 --- a/dirmngr/ks-action.c +++ b/dirmngr/ks-action.c @@ -90,7 +90,7 @@ ks_action_search (ctrl_t ctrl, strlist_t patterns, estream_t outfp) } -/* Get the requested keys (macthing PATTERNS) using all configured +/* Get the requested keys (matching 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) @@ -148,3 +148,36 @@ ks_action_get (ctrl_t ctrl, strlist_t patterns, estream_t outfp) return err; } + + +/* Send an OpenPGP key to all keyservers. The key in {DATA,DATALEN} + is expected in OpenPGP binary transport format. */ +gpg_error_t +ks_action_put (ctrl_t ctrl, const void *data, size_t datalen) +{ + gpg_error_t err = 0; + gpg_error_t first_err = 0; + int any = 0; + uri_item_t uri; + + for (uri = ctrl->keyservers; !err && uri; uri = uri->next) + { + if (uri->parsed_uri->is_http) + { + any = 1; + err = ks_hkp_put (ctrl, uri->parsed_uri, data, datalen); + if (err) + { + first_err = err; + err = 0; + } + } + } + + 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; +} + diff --git a/dirmngr/ks-action.h b/dirmngr/ks-action.h index 4a92ed1a4..b3bd3fc46 100644 --- a/dirmngr/ks-action.h +++ b/dirmngr/ks-action.h @@ -22,6 +22,7 @@ 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); +gpg_error_t ks_action_put (ctrl_t ctrl, const void *data, size_t datalen); #endif /*DIRMNGR_KS_ACTION_H*/ diff --git a/dirmngr/ks-engine-hkp.c b/dirmngr/ks-engine-hkp.c index 662e9e4cb..e25900ae1 100644 --- a/dirmngr/ks-engine-hkp.c +++ b/dirmngr/ks-engine-hkp.c @@ -38,9 +38,12 @@ /* Send an HTTP request. On success returns an estream object at - R_FP. HOSTPORTSTR is only used for diagnostics. */ + R_FP. HOSTPORTSTR is only used for diagnostics. If POST_CB is not + NULL a post request is used and that callback is called to allow + writing the post data. */ static gpg_error_t send_request (ctrl_t ctrl, const char *request, const char *hostportstr, + gpg_error_t (*post_cb)(void *, http_t), void *post_cb_value, estream_t *r_fp) { gpg_error_t err; @@ -51,7 +54,9 @@ send_request (ctrl_t ctrl, const char *request, const char *hostportstr, *r_fp = NULL; once_more: - err = http_open (&http, HTTP_REQ_GET, request, + err = http_open (&http, + post_cb? HTTP_REQ_POST : HTTP_REQ_GET, + request, /* fixme: AUTH */ NULL, 0, /* fixme: proxy*/ NULL, @@ -65,9 +70,14 @@ send_request (ctrl_t ctrl, const char *request, const char *hostportstr, 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 (post_cb) + err = post_cb (post_cb_value, http); + if (!err) + { + http_start_data (http); + if (es_ferror (fp)) + err = gpg_error_from_syserror (); + } } if (err) { @@ -135,19 +145,76 @@ send_request (ctrl_t ctrl, const char *request, const char *hostportstr, /* 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; } +static gpg_error_t +armor_data (char **r_string, const void *data, size_t datalen) +{ + gpg_error_t err; + struct b64state b64state; + estream_t fp; + long length; + char *buffer; + size_t nread; + *r_string = NULL; + + fp = es_fopenmem (0, "rw"); + if (!fp) + return gpg_error_from_syserror (); + + if ((err=b64enc_start_es (&b64state, fp, "PGP PUBLIC KEY BLOCK")) + || (err=b64enc_write (&b64state, data, datalen)) + || (err = b64enc_finish (&b64state))) + { + es_fclose (fp); + return err; + } + + /* FIXME: To avoid the extra buffer allocation estream should + provide a function to snatch the internal allocated memory from + such a memory stream. */ + length = es_ftell (fp); + if (length < 0) + { + err = gpg_error_from_syserror (); + es_fclose (fp); + return err; + } + + buffer = xtrymalloc (length+1); + if (!buffer) + { + err = gpg_error_from_syserror (); + es_fclose (fp); + return err; + } + + es_rewind (fp); + if (es_read (fp, buffer, length, &nread)) + { + err = gpg_error_from_syserror (); + es_fclose (fp); + return err; + } + buffer[nread] = 0; + es_fclose (fp); + + *r_string = buffer; + return 0; +} + + + + /* 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 @@ -251,7 +318,7 @@ ks_hkp_search (ctrl_t ctrl, parsed_uri_t uri, const char *pattern, } /* Send the request. */ - err = send_request (ctrl, request, hostport, &fp); + err = send_request (ctrl, request, hostport, NULL, NULL, &fp); if (err) goto leave; @@ -368,7 +435,7 @@ ks_hkp_get (ctrl_t ctrl, parsed_uri_t uri, const char *keyspec, estream_t *r_fp) } /* Send the request. */ - err = send_request (ctrl, request, hostport, &fp); + err = send_request (ctrl, request, hostport, NULL, NULL, &fp); if (err) goto leave; @@ -384,3 +451,108 @@ ks_hkp_get (ctrl_t ctrl, parsed_uri_t uri, const char *keyspec, estream_t *r_fp) } + + +/* Callback parameters for put_post_cb. */ +struct put_post_parm_s +{ + char *datastring; +}; + + +/* Helper for ks_hkp_put. */ +static gpg_error_t +put_post_cb (void *opaque, http_t http) +{ + struct put_post_parm_s *parm = opaque; + gpg_error_t err = 0; + estream_t fp; + size_t len; + + fp = http_get_write_ptr (http); + len = strlen (parm->datastring); + + es_fprintf (fp, + "Content-Type: application/x-www-form-urlencoded\r\n" + "Content-Length: %zu\r\n", len+8 /* 8 is for "keytext" */); + http_start_data (http); + if (es_fputs ("keytext=", fp) || es_write (fp, parm->datastring, len, NULL)) + err = gpg_error_from_syserror (); + return err; +} + + +/* Send the key in {DATA,DATALEN} to the keyserver identified by URI. */ +gpg_error_t +ks_hkp_put (ctrl_t ctrl, parsed_uri_t uri, const void *data, size_t datalen) +{ + gpg_error_t err; + const char *scheme; + char portstr[10]; + char *hostport = NULL; + char *request = NULL; + estream_t fp = NULL; + struct put_post_parm_s parm; + char *armored = NULL; + + parm.datastring = NULL; + + /* 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 ()*/ + + err = armor_data (&armored, data, datalen); + if (err) + goto leave; + + parm.datastring = http_escape_string (armored, EXTRA_ESCAPE_CHARS); + if (!parm.datastring) + { + err = gpg_error_from_syserror (); + goto leave; + } + xfree (armored); + armored = NULL; + + /* 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/add", NULL); + if (!request) + { + err = gpg_error_from_syserror (); + goto leave; + } + + /* Send the request. */ + err = send_request (ctrl, request, hostport, put_post_cb, &parm, &fp); + if (err) + goto leave; + + leave: + es_fclose (fp); + xfree (parm.datastring); + xfree (armored); + xfree (request); + xfree (hostport); + return err; +} diff --git a/dirmngr/ks-engine.h b/dirmngr/ks-engine.h index 4b26662e8..304fc4d1a 100644 --- a/dirmngr/ks-engine.h +++ b/dirmngr/ks-engine.h @@ -28,6 +28,8 @@ 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); +gpg_error_t ks_hkp_put (ctrl_t ctrl, parsed_uri_t uri, + const void *data, size_t datalen); diff --git a/dirmngr/server.c b/dirmngr/server.c index 5d61da898..fc7b22989 100644 --- a/dirmngr/server.c +++ b/dirmngr/server.c @@ -47,6 +47,12 @@ something reasonable. */ #define MAX_CERT_LENGTH (8*1024) +/* The same goes for OpenPGP keyblocks, but here we need to allow for + much longer blocks; a 200k keyblock is not too unusual for keys + with a lot of signatures (e.g. 0x5b0358a2). */ +#define MAX_KEYBLOCK_LENGTH (512*1024) + + #define PARM_ERROR(t) assuan_set_error (ctx, \ gpg_error (GPG_ERR_ASS_PARAMETER), (t)) #define set_error(e,t) assuan_set_error (ctx, gpg_error (e), (t)) @@ -1535,6 +1541,70 @@ cmd_ks_get (assuan_context_t ctx, char *line) } + +static const char hlp_ks_put[] = + "KS_PUT\n" + "\n" + "Send a key to the configured OpenPGP keyservers. The actual key material\n" + "is then requested by Dirmngr using\n" + "\n" + " INQUIRE KEYBLOCK\n" + "\n" + "The client shall respond with a binary version of the keyblock. For LDAP\n" + "keyservers Dirmngr may ask for meta information of the provided keyblock\n" + "using:\n" + "\n" + " INQUIRE KEYBLOCK_INFO\n" + "\n" + "The client shall respond with a colon delimited info lines"; +static gpg_error_t +cmd_ks_put (assuan_context_t ctx, char *line) +{ + ctrl_t ctrl = assuan_get_pointer (ctx); + gpg_error_t err; + unsigned char *value = NULL; + size_t valuelen; + unsigned char *info = NULL; + size_t infolen; + + /* No options for now. */ + line = skip_options (line); + + /* Ask for the key material. */ + err = assuan_inquire (ctx, "KEYBLOCK", + &value, &valuelen, MAX_KEYBLOCK_LENGTH); + if (err) + { + log_error (_("assuan_inquire failed: %s\n"), gpg_strerror (err)); + goto leave; + } + + if (!valuelen) /* No data returned; return a comprehensible error. */ + { + err = gpg_error (GPG_ERR_MISSING_CERT); + goto leave; + } + + /* Ask for the key meta data. Not actually needed for HKP servers + but we do it anyway test the client implementaion. */ + err = assuan_inquire (ctx, "KEYBLOCK_INFO", + &info, &infolen, MAX_KEYBLOCK_LENGTH); + if (err) + { + log_error (_("assuan_inquire failed: %s\n"), gpg_strerror (err)); + goto leave; + } + + /* Send the key. */ + err = ks_action_put (ctrl, value, valuelen); + + leave: + xfree (info); + xfree (value); + return leave_cmd (ctx, err); +} + + static const char hlp_getinfo[] = @@ -1672,6 +1742,7 @@ register_commands (assuan_context_t ctx) { "KEYSERVER", cmd_keyserver, hlp_keyserver }, { "KS_SEARCH", cmd_ks_search, hlp_ks_search }, { "KS_GET", cmd_ks_get, hlp_ks_get }, + { "KS_PUT", cmd_ks_put, hlp_ks_put }, { "GETINFO", cmd_getinfo, hlp_getinfo }, { "KILLDIRMNGR",cmd_killdirmngr,hlp_killdirmngr }, { "RELOADDIRMNGR",cmd_reloaddirmngr,hlp_reloaddirmngr }, diff --git a/g10/ChangeLog b/g10/ChangeLog index 27f60fe24..a60d5d581 100644 --- a/g10/ChangeLog +++ b/g10/ChangeLog @@ -1,13 +1,12 @@ -2011-01-18 Werner Koch +2011-01-20 Werner Koch + + * 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. -2011-01-14 Werner Koch - - * keyserver.c (parse_keyrec): Use trim_trailing_ws. - -2011-01-07 Werner Koch - * call-dirmngr.c, call-dirmngr.h: New. * gpg.h (server_control_s): Add DIRMNGR_LOCAL. * gpg.c: Include call-dirmngr.h. @@ -11671,7 +11670,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/call-dirmngr.c b/g10/call-dirmngr.c index 1816a72d3..f34b94b60 100644 --- a/g10/call-dirmngr.c +++ b/g10/call-dirmngr.c @@ -59,6 +59,16 @@ struct ks_get_parm_s }; +/* 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 @@ -436,3 +446,166 @@ gpg_dirmngr_ks_get (ctrl_t ctrl, char **pattern, estream_t *r_fp) 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 index c848a2902..9523bf568 100644 --- a/g10/call-dirmngr.h +++ b/g10/call-dirmngr.h @@ -25,6 +25,8 @@ 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 91c6a73d7..43856ffea 100644 --- a/g10/export.c +++ b/g10/export.c @@ -114,6 +114,60 @@ export_pubkeys_stream (ctrl_t ctrl, iobuf_t out, strlist_t users, return rc; } + +/* + * Export a single key into a memory buffer. + */ +gpg_error_t +export_pubkey_buffer (ctrl_t ctrl, const char *keyspec, unsigned int options, + kbnode_t *r_keyblock, void **r_data, size_t *r_datalen) +{ + gpg_error_t err; + iobuf_t iobuf; + int any; + strlist_t helplist; + + *r_keyblock = NULL; + *r_data = NULL; + *r_datalen = 0; + + helplist = NULL; + if (!add_to_strlist_try (&helplist, keyspec)) + return gpg_error_from_syserror (); + + iobuf = iobuf_temp (); + err = do_export_stream (ctrl, iobuf, helplist, 0, r_keyblock, options, &any); + if (!err && !any) + err = gpg_error (GPG_ERR_NOT_FOUND); + if (!err) + { + const void *src; + size_t datalen; + + iobuf_flush_temp (iobuf); + src = iobuf_get_temp_buffer (iobuf); + datalen = iobuf_get_temp_length (iobuf); + if (!datalen) + err = gpg_error (GPG_ERR_NO_PUBKEY); + else if (!(*r_data = xtrymalloc (datalen))) + err = gpg_error_from_syserror (); + else + { + memcpy (*r_data, src, datalen); + *r_datalen = datalen; + } + } + iobuf_close (iobuf); + free_strlist (helplist); + if (err && *r_keyblock) + { + release_kbnode (*r_keyblock); + *r_keyblock = NULL; + } + return err; +} + + int export_seckeys (ctrl_t ctrl, strlist_t users ) { diff --git a/g10/keyserver.c b/g10/keyserver.c index e560d4bfa..2f055ada5 100644 --- a/g10/keyserver.c +++ b/g10/keyserver.c @@ -111,13 +111,11 @@ 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 */ @@ -989,664 +987,6 @@ search_line_handler (void *opaque, char *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; -} - -/* 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) -{ - 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 (); - - 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 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'; - } - 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) - { - 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); - } - 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 - { - 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;itochild,"0x"); - - for(f=0;ftochild,"%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;rlen;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; - } - - default: - log_fatal(_("no keyserver action!\n")); - break; - } - - /* Done sending, so start reading. */ - ret=exec_read(spawn); - if(ret) - goto fail; - - /* Now handle the response */ - - for(;;) - { - 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 */ - } - - if(!gotversion) - { - log_error(_("keyserver did not send VERSION\n")); - goto fail; - } - - 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; -} - - - - -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 gpg_error (GPG_ERR_BAD_URI); - } - - - 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; -} - - - - int keyserver_export (ctrl_t ctrl, strlist_t users) @@ -1674,7 +1014,7 @@ 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); } @@ -2113,26 +1453,6 @@ keyserver_search (ctrl_t ctrl, strlist_t tokens) /* 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); @@ -2267,6 +1587,58 @@ keyserver_get (ctrl_t ctrl, KEYDB_SEARCH_DESC *desc, int ndesc, } +/* 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; + +} @@ -2439,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); @@ -2513,8 +1886,9 @@ keyserver_import_ldap (ctrl_t ctrl, 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 2e760844a..427834023 100644 --- a/g10/main.h +++ b/g10/main.h @@ -287,6 +287,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/keyserver/ChangeLog b/keyserver/ChangeLog index 14d5f6244..cc42a6426 100644 --- a/keyserver/ChangeLog +++ b/keyserver/ChangeLog @@ -1,3 +1,9 @@ +2011-01-20 Werner Koch + + * gpgkeys_hkp.c (get_name): Remove test for KS_GETNAME. It is + always true. + (search_key): Remove test for KS_GETNAME. It is always false. + 2009-08-26 Werner Koch * gpgkeys_hkp.c: Include util.h. diff --git a/keyserver/gpgkeys_hkp.c b/keyserver/gpgkeys_hkp.c index dd2129051..d43a61ab5 100644 --- a/keyserver/gpgkeys_hkp.c +++ b/keyserver/gpgkeys_hkp.c @@ -340,7 +340,7 @@ get_name(const char *getkey) opt->path, appendable_path (opt->path,"/pks/lookup?op=get&options=mr&search="), searchkey_encoded, - opt->action == KS_GETNAME? "&exact=on":"", + "&exact=on", NULL); if(!request) { @@ -429,7 +429,6 @@ search_key(const char *searchkey) appendable_path (opt->path, "/pks/lookup?op=index&options=mr&search="), hexprefix, searchkey_encoded, - opt->action == KS_GETNAME? "&exact=on":"", NULL); if(!request) {