wkd: New option --add-revocs and some fixes.

* tools/gpg-wks.h (opt): Add add_revocs.
* tools/wks-util.c (wks_get_key): Add arg 'binary'.
(wks_armor_key): New.
(wks_find_add_revocs): New.
(wks_cmd_install_key): Get key in binary mode and add revocations if
enabled.
* tools/gpg-wks-client.c (oAddRevocs): New.
(opts): Add --add-revocs.
(parse_arguments): Set option,
(command_send): Get key in binary mode, add revocations if enabled,
and explictly armor key.  Remove kludge to skip the Content-type line
in no_encrypt mode.

(mirror_one_keys_userid): Always filter the key to get rid of the
armor as received from dirmngr.  Add revocations from the local
keyring.
--

Note that this also fixes an oddity of the new mirror command which
used to store the keys armored as received from dirmngr.
This commit is contained in:
Werner Koch 2022-11-29 16:47:44 +01:00
parent fbc52f5501
commit c3f9f2d497
No known key found for this signature in database
GPG Key ID: E3FDFF218E45B72B
4 changed files with 232 additions and 37 deletions

View File

@ -213,6 +213,14 @@ operation. The format of @var{file} is one mail address (just the
addrspec, e.g. "postel@@isi.edu") per line. Empty lines and lines
starting with a '#' are ignored.
@item --add-revocs
@opindex add-revocs
If enabled append revocation certificates for the same addrspec as
used in the WKD to the key. Modern gpg version are able to import and
apply them for existing keys. Note that when used with the
@option{--mirror} command the revocation are searched in the local
keyring and not in an LDAP directory.
@item --verbose
@opindex verbose
Enable extra informational output.

View File

@ -76,6 +76,7 @@ enum cmd_and_opt_values
oWithColons,
oBlacklist,
oNoAutostart,
oAddRevocs,
oDummy
};
@ -102,9 +103,9 @@ static gpgrt_opt_t opts[] = {
ARGPARSE_c (aRemoveKey, "remove-key",
"remove a key from a directory"),
ARGPARSE_c (aPrintWKDHash, "print-wkd-hash",
"Print the WKD identifier for the given user ids"),
"print the WKD identifier for the given user ids"),
ARGPARSE_c (aPrintWKDURL, "print-wkd-url",
"Print the WKD URL for the given user id"),
"print the WKD URL for the given user id"),
ARGPARSE_group (301, ("@\nOptions:\n ")),
@ -119,6 +120,7 @@ static gpgrt_opt_t opts[] = {
ARGPARSE_s_n (oWithColons, "with-colons", "@"),
ARGPARSE_s_s (oBlacklist, "blacklist", "@"),
ARGPARSE_s_s (oDirectory, "directory", "@"),
ARGPARSE_s_n (oAddRevocs, "add-revocs", "add revocation certificates"),
ARGPARSE_s_s (oFakeSubmissionAddr, "fake-submission-addr", "@"),
@ -257,6 +259,9 @@ parse_arguments (gpgrt_argparse_t *pargs, gpgrt_opt_t *popts)
case oBlacklist:
add_blacklist (pargs->r.ret_str);
break;
case oAddRevocs:
opt.add_revocs = 1;
break;
case aSupported:
case aCreate:
@ -1164,7 +1169,7 @@ command_send (const char *fingerprint, const char *userid)
err = gpg_error (GPG_ERR_INV_USER_ID);
goto leave;
}
err = wks_get_key (&key, fingerprint, addrspec, 0);
err = wks_get_key (&key, fingerprint, addrspec, 0, 1);
if (err)
goto leave;
@ -1232,7 +1237,7 @@ command_send (const char *fingerprint, const char *userid)
estream_t newkey;
es_rewind (key);
err = wks_filter_uid (&newkey, key, thisuid->uid, 0);
err = wks_filter_uid (&newkey, key, thisuid->uid, 1);
if (err)
{
log_error ("error filtering key: %s\n", gpg_strerror (err));
@ -1257,11 +1262,47 @@ command_send (const char *fingerprint, const char *userid)
* the key again. */
es_fclose (key);
key = NULL;
err = wks_get_key (&key, fingerprint, addrspec, 1);
err = wks_get_key (&key, fingerprint, addrspec, 1, 1);
if (err)
goto leave;
}
if (opt.add_revocs)
{
if (es_fseek (key, 0, SEEK_END))
{
err = gpg_error_from_syserror ();
log_error ("error seeking stream: %s\n", gpg_strerror (err));
goto leave;
}
err = wks_find_add_revocs (key, addrspec);
if (err)
{
log_error ("error finding revocations for '%s': %s\n",
addrspec, gpg_strerror (err));
goto leave;
}
}
/* Now put the armor around the key. */
{
estream_t newkey;
es_rewind (key);
err = wks_armor_key (&newkey, key,
no_encrypt? NULL
/* */ : ("Content-Type: application/pgp-keys\n"
"\n"));
if (err)
{
log_error ("error armoring key: %s\n", gpg_strerror (err));
goto leave;
}
es_fclose (key);
key = newkey;
}
/* Hack to support posteo but let them disable this by setting the
* new policy-version flag. */
if (policy->protocol_version < 3
@ -1306,7 +1347,7 @@ command_send (const char *fingerprint, const char *userid)
if (no_encrypt)
{
void *data;
size_t datalen, n;
size_t datalen;
if (posteo_hack)
{
@ -1331,16 +1372,7 @@ command_send (const char *fingerprint, const char *userid)
goto leave;
}
key = NULL;
/* We need to skip over the first line which has a content-type
* header not needed here. */
for (n=0; n < datalen ; n++)
if (((const char *)data)[n] == '\n')
{
n++;
break;
}
err = mime_maker_add_body_data (mime, (char*)data + n, datalen - n);
err = mime_maker_add_body_data (mime, data, datalen);
xfree (data);
if (err)
goto leave;
@ -1827,7 +1859,7 @@ domain_matches_mbox (const char *domain, const char *mbox)
/* Core of mirror_one_key with the goal of mirroring just one uid.
* UIDLIST is used to figure out whether the given MBOX occurs several
* times in UIDLIST and then to single out the newwest one. This is
* times in UIDLIST and then to single out the newest one. This is
* so that for a key with
* uid: Joe Someone <joe@example.org>
* uid: Joe <joe@example.org>
@ -1868,24 +1900,36 @@ mirror_one_keys_userid (estream_t key, const char *mbox, uidinfo_list_t uidlist,
err = gpg_error (GPG_ERR_NO_USER_ID);
goto leave;
}
/* FIXME: Consult blacklist. */
/* Only if we have more than one user id we bother to run the
* filter. In this case the result will be put into NEWKEY*/
/* Always filter the key so that the result will be non-armored. */
es_rewind (key);
if (uidlist->next)
err = wks_filter_uid (&newkey, key, thisuid->uid, 1);
if (err)
{
err = wks_filter_uid (&newkey, key, thisuid->uid, 0);
if (err)
{
log_error ("error filtering key %s: %s\n", fpr, gpg_strerror (err));
err = gpg_error (GPG_ERR_NO_PUBKEY);
goto leave;
}
log_error ("error filtering key %s: %s\n", fpr, gpg_strerror (err));
err = gpg_error (GPG_ERR_NO_PUBKEY);
goto leave;
}
err = wks_install_key_core (newkey? newkey : key, mbox);
if (opt.add_revocs)
{
if (es_fseek (newkey, 0, SEEK_END))
{
err = gpg_error_from_syserror ();
log_error ("error seeking stream: %s\n", gpg_strerror (err));
goto leave;
}
err = wks_find_add_revocs (newkey, mbox);
if (err)
{
log_error ("error finding revocations for '%s': %s\n",
mbox, gpg_strerror (err));
goto leave;
}
es_rewind (newkey);
}
err = wks_install_key_core (newkey, mbox);
if (opt.verbose)
log_info ("key %s published for '%s'\n", fpr, mbox);
mirror_one_key_parm.nuids++;

View File

@ -39,6 +39,7 @@ struct
int use_sendmail;
int with_colons;
int no_autostart;
int add_revocs;
const char *output;
const char *gpg_program;
const char *directory;
@ -91,11 +92,14 @@ void wks_set_status_fd (int fd);
void wks_write_status (int no, const char *format, ...) GPGRT_ATTR_PRINTF(2,3);
void free_uidinfo_list (uidinfo_list_t list);
gpg_error_t wks_get_key (estream_t *r_key, const char *fingerprint,
const char *addrspec, int exact);
const char *addrspec, int exact, int binary);
gpg_error_t wks_list_key (estream_t key, char **r_fpr,
uidinfo_list_t *r_mboxes);
gpg_error_t wks_filter_uid (estream_t *r_newkey, estream_t key,
const char *uid, int binary);
gpg_error_t wks_armor_key (estream_t *r_newkey, estream_t key,
const char *prefix);
gpg_error_t wks_find_add_revocs (estream_t key, const char *addrspec);
gpg_error_t wks_send_mime (mime_maker_t mime);
gpg_error_t wks_parse_policy (policy_flags_t flags, estream_t stream,
int ignore_unknown);

View File

@ -193,10 +193,11 @@ get_key_status_cb (void *opaque, const char *keyword, char *args)
* mail address ADDRSPEC is included in the key. If EXACT is set the
* returned user id must match Addrspec exactly and not just in the
* addr-spec (mailbox) part. The key is returned as a new memory
* stream at R_KEY. */
* stream at R_KEY. If BINARY is set the returned key is
* non-armored. */
gpg_error_t
wks_get_key (estream_t *r_key, const char *fingerprint, const char *addrspec,
int exact)
int exact, int binary)
{
gpg_error_t err;
ccparray_t ccp;
@ -218,8 +219,9 @@ wks_get_key (estream_t *r_key, const char *fingerprint, const char *addrspec,
}
/* Prefix the key with the MIME content type. */
es_fputs ("Content-Type: application/pgp-keys\n"
"\n", key);
if (!binary)
es_fputs ("Content-Type: application/pgp-keys\n"
"\n", key);
filterexp = es_bsprintf ("keep-uid=%s= %s", exact? "uid":"mbox", addrspec);
if (!filterexp)
@ -239,7 +241,8 @@ wks_get_key (estream_t *r_key, const char *fingerprint, const char *addrspec,
ccparray_put (&ccp, "--batch");
ccparray_put (&ccp, "--status-fd=2");
ccparray_put (&ccp, "--always-trust");
ccparray_put (&ccp, "--armor");
if (!binary)
ccparray_put (&ccp, "--armor");
ccparray_put (&ccp, "--export-options=export-minimal");
ccparray_put (&ccp, "--export-filter");
ccparray_put (&ccp, filterexp);
@ -550,6 +553,124 @@ wks_filter_uid (estream_t *r_newkey, estream_t key, const char *uid,
}
/* Put the ascii-armor around KEY and return that as a new estream
* object at R_NEWKEY. Caller must make sure that KEY has been seeked
* to the right position (usually by calling es_rewind). The
* resulting NEWKEY has already been rewound. If PREFIX is not NULL,
* its content is written to NEWKEY propr to the armor; this may be
* used for MIME headers. */
gpg_error_t
wks_armor_key (estream_t *r_newkey, estream_t key, const char *prefix)
{
gpg_error_t err;
estream_t newkey;
struct b64state b64state;
char buffer[4096];
size_t nread;
*r_newkey = NULL;
newkey = es_fopenmem (0, "w+b");
if (!newkey)
{
err = gpg_error_from_syserror ();
return err;
}
if (prefix)
es_fputs (prefix, newkey);
err = b64enc_start_es (&b64state, newkey, "PGP PUBLIC KEY BLOCK");
if (err)
goto leave;
do
{
nread = es_fread (buffer, 1, sizeof buffer, key);
if (!nread)
break;
err = b64enc_write (&b64state, buffer, nread);
if (err)
goto leave;
}
while (!es_feof (key) && !es_ferror (key));
if (!es_feof (key) || es_ferror (key))
{
err = gpg_error_from_syserror ();
goto leave;
}
err = b64enc_finish (&b64state);
if (err)
goto leave;
es_rewind (newkey);
*r_newkey = newkey;
newkey = NULL;
leave:
es_fclose (newkey);
return err;
}
/* Run gpg to export the revocation certificates for ADDRSPEC. Add
* them to KEY which is expected to be non-armored keyblock. */
gpg_error_t
wks_find_add_revocs (estream_t key, const char *addrspec)
{
gpg_error_t err;
ccparray_t ccp;
const char **argv = NULL;
char *filterexp = NULL;
filterexp = es_bsprintf ("select=mbox= %s", addrspec);
if (!filterexp)
{
err = gpg_error_from_syserror ();
log_error ("error allocating memory buffer: %s\n", gpg_strerror (err));
goto leave;
}
ccparray_init (&ccp, 0);
ccparray_put (&ccp, "--no-options");
if (opt.verbose < 2)
ccparray_put (&ccp, "--quiet");
else
ccparray_put (&ccp, "--verbose");
ccparray_put (&ccp, "--batch");
ccparray_put (&ccp, "--status-fd=2");
ccparray_put (&ccp, "--export-options=export-revocs");
ccparray_put (&ccp, "--export-filter");
ccparray_put (&ccp, filterexp);
ccparray_put (&ccp, "--export");
ccparray_put (&ccp, addrspec);
ccparray_put (&ccp, NULL);
argv = ccparray_get (&ccp, NULL);
if (!argv)
{
err = gpg_error_from_syserror ();
goto leave;
}
debug_gpg_invocation (__func__, argv);
err = gnupg_exec_tool_stream (opt.gpg_program, argv, NULL,
NULL, key,
key_status_cb, NULL);
if (err)
{
log_error ("exporting revocs failed: %s\n", gpg_strerror (err));
goto leave;
}
leave:
xfree (filterexp);
xfree (argv);
return err;
}
/* Helper to write mail to the output(s). */
gpg_error_t
wks_send_mime (mime_maker_t mime)
@ -1121,7 +1242,7 @@ wks_cmd_install_key (const char *fname, const char *userid)
{
/* FNAME looks like a fingerprint. Get the key from the
* standard keyring. */
err = wks_get_key (&fp, fname, addrspec, 0);
err = wks_get_key (&fp, fname, addrspec, 0, 1);
if (err)
{
log_error ("error getting key '%s' (uid='%s'): %s\n",
@ -1193,6 +1314,24 @@ wks_cmd_install_key (const char *fname, const char *userid)
fp = fp2;
}
if (opt.add_revocs)
{
if (es_fseek (fp, 0, SEEK_END))
{
err = gpg_error_from_syserror ();
log_error ("error seeking stream: %s\n", gpg_strerror (err));
goto leave;
}
err = wks_find_add_revocs (fp, addrspec);
if (err)
{
log_error ("error finding revocations for '%s': %s\n",
addrspec, gpg_strerror (err));
goto leave;
}
es_rewind (fp);
}
err = wks_install_key_core (fp, addrspec);
if (!opt.quiet)
log_info ("key %s published for '%s'\n", fpr, addrspec);