aboutsummaryrefslogtreecommitdiffstats
path: root/dirmngr
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--dirmngr/ChangeLog8
-rw-r--r--dirmngr/ks-action.c35
-rw-r--r--dirmngr/ks-action.h1
-rw-r--r--dirmngr/ks-engine-hkp.c190
-rw-r--r--dirmngr/ks-engine.h2
-rw-r--r--dirmngr/server.c71
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 },