aboutsummaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/gpg-wks-client.c118
-rw-r--r--tools/gpg-wks.h8
-rw-r--r--tools/wks-util.c224
3 files changed, 310 insertions, 40 deletions
diff --git a/tools/gpg-wks-client.c b/tools/gpg-wks-client.c
index 3aa8f98c4..2139011e0 100644
--- a/tools/gpg-wks-client.c
+++ b/tools/gpg-wks-client.c
@@ -74,6 +74,7 @@ enum cmd_and_opt_values
oWithColons,
oBlacklist,
oNoAutostart,
+ oAddRevocs,
oDummy
};
@@ -100,9 +101,9 @@ static ARGPARSE_OPTS 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 ")),
@@ -117,6 +118,7 @@ static ARGPARSE_OPTS 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", "@"),
@@ -254,6 +256,9 @@ parse_arguments (ARGPARSE_ARGS *pargs, ARGPARSE_OPTS *popts)
case oBlacklist:
add_blacklist (pargs->r.ret_str);
break;
+ case oAddRevocs:
+ opt.add_revocs = 1;
+ break;
case aSupported:
case aCreate:
@@ -1090,6 +1095,9 @@ command_check (char *userid)
log_info (" created: %s\n", asctimestamp (sl->created));
if (sl->mbox)
log_info (" addr-spec: %s\n", sl->mbox);
+ if (sl->expired || sl->revoked)
+ log_info (" flags:%s%s\n",
+ sl->expired? " expired":"", sl->revoked?" revoked":"");
}
}
if (!found)
@@ -1128,6 +1136,7 @@ command_send (const char *fingerprint, const char *userid)
uidinfo_list_t uidlist = NULL;
uidinfo_list_t uid, thisuid;
time_t thistime;
+ int any;
if (classify_user_id (fingerprint, &desc, 1)
|| !(desc.mode == KEYDB_SEARCH_MODE_FPR
@@ -1145,7 +1154,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;
@@ -1189,12 +1198,20 @@ command_send (const char *fingerprint, const char *userid)
}
thistime = 0;
thisuid = NULL;
+ any = 0;
for (uid = uidlist; uid; uid = uid->next)
{
if (!uid->mbox)
continue; /* Should not happen anyway. */
if (policy->mailbox_only && ascii_strcasecmp (uid->uid, uid->mbox))
continue; /* UID has more than just the mailbox. */
+ if (uid->expired)
+ {
+ if (opt.verbose)
+ log_info ("ignoring expired user id '%s'\n", uid->uid);
+ continue;
+ }
+ any = 1;
if (uid->created > thistime)
{
thistime = uid->created;
@@ -1203,6 +1220,14 @@ command_send (const char *fingerprint, const char *userid)
}
if (!thisuid)
thisuid = uidlist; /* This is the case for a missing timestamp. */
+ if (!any)
+ {
+ log_error ("public key %s has no mail address '%s'\n",
+ fingerprint, addrspec);
+ err = gpg_error (GPG_ERR_INV_USER_ID);
+ goto leave;
+ }
+
if (opt.verbose)
log_info ("submitting key with user id '%s'\n", thisuid->uid);
@@ -1213,7 +1238,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));
@@ -1238,11 +1263,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
@@ -1287,7 +1348,7 @@ command_send (const char *fingerprint, const char *userid)
if (no_encrypt)
{
void *data;
- size_t datalen, n;
+ size_t datalen;
if (posteo_hack)
{
@@ -1312,16 +1373,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;
@@ -1808,7 +1860,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 <[email protected]>
* uid: Joe <[email protected]>
@@ -1849,24 +1901,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)
+ {
+ log_error ("error filtering key %s: %s\n", fpr, gpg_strerror (err));
+ err = gpg_error (GPG_ERR_NO_PUBKEY);
+ goto leave;
+ }
+
+ if (opt.add_revocs)
{
- err = wks_filter_uid (&newkey, key, thisuid->uid, 0);
+ 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 filtering key %s: %s\n", fpr, gpg_strerror (err));
- err = gpg_error (GPG_ERR_NO_PUBKEY);
+ log_error ("error finding revocations for '%s': %s\n",
+ mbox, gpg_strerror (err));
goto leave;
}
+ es_rewind (newkey);
}
- err = wks_install_key_core (newkey? newkey : key, mbox);
+ 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++;
@@ -1905,6 +1969,8 @@ mirror_one_key (estream_t key)
{
if (!uid->mbox || (uid->flags & 1))
continue; /* No mail box or already processed. */
+ if (uid->expired)
+ continue;
if (!domain_matches_mbox (domain, uid->mbox))
continue; /* We don't want this one. */
if (is_in_blacklist (uid->mbox))
diff --git a/tools/gpg-wks.h b/tools/gpg-wks.h
index 32aa8c328..a7c17ca52 100644
--- a/tools/gpg-wks.h
+++ b/tools/gpg-wks.h
@@ -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;
@@ -80,6 +81,8 @@ struct uidinfo_list_s
time_t created; /* Time the userid was created. */
char *mbox; /* NULL or the malloced mailbox from UID. */
unsigned int flags; /* These flags are cleared on creation. */
+ unsigned int expired:1;
+ unsigned int revoked:1;
char uid[1];
};
typedef struct uidinfo_list_s *uidinfo_list_t;
@@ -91,11 +94,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);
diff --git a/tools/wks-util.c b/tools/wks-util.c
index 3044fe2f8..2e8541491 100644
--- a/tools/wks-util.c
+++ b/tools/wks-util.c
@@ -101,7 +101,8 @@ wks_write_status (int no, const char *format, ...)
* updated. C-style escaping is removed from UID. On error ERRNO is
* set and NULL returned. */
static uidinfo_list_t
-append_to_uidinfo_list (uidinfo_list_t *list, const char *uid, time_t created)
+append_to_uidinfo_list (uidinfo_list_t *list, const char *uid, time_t created,
+ int expired, int revoked)
{
uidinfo_list_t r, sl;
char *plainuid;
@@ -121,6 +122,8 @@ append_to_uidinfo_list (uidinfo_list_t *list, const char *uid, time_t created)
sl->created = created;
sl->flags = 0;
sl->mbox = mailbox_from_userid (plainuid);
+ sl->expired = !!expired;
+ sl->revoked = !!revoked;
sl->next = NULL;
if (!*list)
*list = sl;
@@ -150,6 +153,21 @@ free_uidinfo_list (uidinfo_list_t list)
}
+static void
+debug_gpg_invocation (const char *func, const char **argv)
+{
+ int i;
+
+ if (!(opt.debug & DBG_EXTPROG_VALUE))
+ return;
+
+ log_debug ("%s: exec '%s' with", func, opt.gpg_program);
+ for (i=0; argv[i]; i++)
+ log_printf (" '%s'", argv[i]);
+ log_printf ("\n");
+}
+
+
struct get_key_status_parm_s
{
@@ -164,7 +182,8 @@ get_key_status_cb (void *opaque, const char *keyword, char *args)
{
struct get_key_status_parm_s *parm = opaque;
- /*log_debug ("%s: %s\n", keyword, args);*/
+ if (DBG_CRYPTO)
+ log_debug ("%s: %s\n", keyword, args);
if (!strcmp (keyword, "EXPORTED"))
{
parm->count++;
@@ -177,10 +196,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;
@@ -202,8 +222,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)
@@ -223,7 +244,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);
@@ -239,6 +261,7 @@ wks_get_key (estream_t *r_key, const char *fingerprint, const char *addrspec,
goto leave;
}
parm.fpr = fingerprint;
+ debug_gpg_invocation (__func__, argv);
err = gnupg_exec_tool_stream (opt.gpg_program, argv, NULL,
NULL, key,
get_key_status_cb, &parm);
@@ -276,6 +299,22 @@ key_status_cb (void *opaque, const char *keyword, char *args)
}
+/* Parse field 1 and set revoked and expired on return. */
+static void
+set_expired_revoked (const char *string, int *expired, int *revoked)
+{
+ *expired = *revoked = 0;
+ /* Look at letters and stop at the first digit. */
+ for ( ;*string && !digitp (string); string++)
+ {
+ if (*string == 'e')
+ *expired = 1;
+ else if (*string == 'r')
+ *revoked = 1;
+ }
+}
+
+
/* Run gpg on KEY and store the primary fingerprint at R_FPR and the
* list of mailboxes at R_MBOXES. Returns 0 on success; on error NULL
* is stored at R_FPR and R_MBOXES and an error code is returned.
@@ -296,6 +335,7 @@ wks_list_key (estream_t key, char **r_fpr, uidinfo_list_t *r_mboxes)
int lnr;
char *fpr = NULL;
uidinfo_list_t mboxes = NULL;
+ int expired, revoked;
if (r_fpr)
*r_fpr = NULL;
@@ -332,6 +372,7 @@ wks_list_key (estream_t key, char **r_fpr, uidinfo_list_t *r_mboxes)
err = gpg_error_from_syserror ();
goto leave;
}
+ debug_gpg_invocation (__func__, argv);
err = gnupg_exec_tool_stream (opt.gpg_program, argv, key,
NULL, listing,
key_status_cb, NULL);
@@ -343,6 +384,7 @@ wks_list_key (estream_t key, char **r_fpr, uidinfo_list_t *r_mboxes)
es_rewind (listing);
lnr = 0;
+ expired = revoked = 0;
maxlen = 2048; /* Set limit. */
while ((len = es_read_line (listing, &line, &length_of_line, &maxlen)) > 0)
{
@@ -387,12 +429,20 @@ wks_list_key (estream_t key, char **r_fpr, uidinfo_list_t *r_mboxes)
err = gpg_error (GPG_ERR_INV_ENGINE);
goto leave;
}
- if (lnr > 1 && !strcmp (fields[0], "pub"))
+ if (!strcmp (fields[0], "pub"))
{
- /* More than one public key. */
- err = gpg_error (GPG_ERR_TOO_MANY);
- goto leave;
+ if (lnr > 1)
+ {
+ /* More than one public key. */
+ err = gpg_error (GPG_ERR_TOO_MANY);
+ goto leave;
+ }
+ if (nfields > 1)
+ set_expired_revoked (fields[1], &expired, &revoked);
+ else
+ expired = revoked = 0;
}
+
if (!strcmp (fields[0], "sub") || !strcmp (fields[0], "ssb"))
break; /* We can stop parsing here. */
@@ -407,8 +457,13 @@ wks_list_key (estream_t key, char **r_fpr, uidinfo_list_t *r_mboxes)
}
else if (!strcmp (fields[0], "uid") && nfields > 9)
{
+ int uidexpired, uidrevoked;
+
+ set_expired_revoked (fields[1], &uidexpired, &uidrevoked);
if (!append_to_uidinfo_list (&mboxes, fields[9],
- parse_timestamp (fields[5], NULL)))
+ parse_timestamp (fields[5], NULL),
+ expired || uidexpired,
+ revoked || uidrevoked))
{
err = gpg_error_from_syserror ();
goto leave;
@@ -510,6 +565,7 @@ wks_filter_uid (estream_t *r_newkey, estream_t key, const char *uid,
err = gpg_error_from_syserror ();
goto leave;
}
+ debug_gpg_invocation (__func__, argv);
err = gnupg_exec_tool_stream (opt.gpg_program, argv, key,
NULL, newkey,
key_status_cb, NULL);
@@ -531,6 +587,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)
@@ -1103,7 +1277,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",
@@ -1140,6 +1314,12 @@ wks_cmd_install_key (const char *fname, const char *userid)
continue; /* Should not happen anyway. */
if (ascii_strcasecmp (uid->mbox, addrspec))
continue; /* Not the requested addrspec. */
+ if (uid->expired)
+ {
+ if (opt.verbose)
+ log_info ("ignoring expired user id '%s'\n", uid->uid);
+ continue;
+ }
any = 1;
if (uid->created > thistime)
{
@@ -1175,6 +1355,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);