diff options
Diffstat (limited to '')
-rw-r--r-- | dirmngr/ChangeLog | 8 | ||||
-rw-r--r-- | dirmngr/ks-action.c | 35 | ||||
-rw-r--r-- | dirmngr/ks-action.h | 1 | ||||
-rw-r--r-- | dirmngr/ks-engine-hkp.c | 190 | ||||
-rw-r--r-- | dirmngr/ks-engine.h | 2 | ||||
-rw-r--r-- | dirmngr/server.c | 71 |
6 files changed, 291 insertions, 16 deletions
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 <[email protected]> +2011-01-20 Werner Koch <[email protected]> * 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 <[email protected]> - * 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 }, |