From 7f7f5d06fa5aa3a3c5ab8d2e59ee76207bfdeaa0 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Mon, 18 Sep 2017 12:52:20 +0200 Subject: [PATCH] wks: Send only the newest UID to the server. * tools/wks-util.c (list_key_status_cb): Rename to key_status_cb. (wks_filter_uid): New. (wks_list_key): Allow FPR to be NULL. Return an error if no fingerprint was found. * tools/gpg-wks-server.c (process_new_key) (check_and_publish): Remove now useless extra check for FPR. * tools/gpg-wks-client.c (command_check): Ditto. (command_send): Filter out the newest uid. -- This fixes the case of having several userids with all the the same mailbox. Now we use the latest user id created. This patch is also a prerequisite to automatically create a new user id for providers with the mailbox-only policy. Signed-off-by: Werner Koch --- tools/gpg-wks-client.c | 60 ++++++++++++++++++++++-- tools/gpg-wks-server.c | 14 +----- tools/gpg-wks.h | 2 + tools/wks-util.c | 104 ++++++++++++++++++++++++++++++++++++++--- 4 files changed, 158 insertions(+), 22 deletions(-) diff --git a/tools/gpg-wks-client.c b/tools/gpg-wks-client.c index 18a0edd72..37b75606b 100644 --- a/tools/gpg-wks-client.c +++ b/tools/gpg-wks-client.c @@ -644,10 +644,9 @@ command_check (char *userid) /* Look closer at the key. */ err = wks_list_key (key, &fpr, &mboxes); - if (err || !fpr) + if (err) { - log_error ("error parsing key: %s\n", - err? gpg_strerror (err) : "no fingerprint found"); + log_error ("error parsing key: %s\n", gpg_strerror (err)); err = gpg_error (GPG_ERR_NO_PUBKEY); goto leave; } @@ -700,6 +699,9 @@ command_send (const char *fingerprint, const char *userid) int no_encrypt = 0; int posteo_hack = 0; const char *domain; + uidinfo_list_t uidlist = NULL; + uidinfo_list_t uid, thisuid; + time_t thistime; memset (&policy, 0, sizeof policy); @@ -769,6 +771,57 @@ command_send (const char *fingerprint, const char *userid) if (policy.auth_submit) log_info ("no confirmation required for '%s'\n", addrspec); + /* In case the key has several uids with the same addr-spec we will + * use the newest one. */ + err = wks_list_key (key, NULL, &uidlist); + if (err) + { + log_error ("error parsing key: %s\n",gpg_strerror (err)); + err = gpg_error (GPG_ERR_NO_PUBKEY); + goto leave; + } + thistime = 0; + thisuid = NULL; + for (uid = uidlist; uid; uid = uid->next) + { + if (!uid->mbox) + continue; /* Should not happen anyway. */ + if (uid->created > thistime) + { + thistime = uid->created; + thisuid = uid; + } + } + if (!thisuid) + thisuid = uid; /* This is the case for a missing timestamp. */ + if (opt.verbose) + log_info ("submitting key with user id '%s'\n", thisuid->uid); + + /* If we have more than one user id we need to filter the key to + * include only THISUID. */ + if (uidlist->next) + { + estream_t newkey; + + es_rewind (key); + err = wks_filter_uid (&newkey, key, thisuid->uid); + if (err) + { + log_error ("error filtering key: %s\n", gpg_strerror (err)); + err = gpg_error (GPG_ERR_NO_PUBKEY); + goto leave; + } + es_fclose (key); + key = newkey; + } + + if (policy.mailbox_only + && ascii_strcasecmp (userid, addrspec)) + { + log_info ("Warning: policy requires 'mailbox-only'" + " - creating new user id'\n"); + } + /* Hack to support posteo but let them disable this by setting the * new policy-version flag. */ if (policy.protocol_version < 3 @@ -885,6 +938,7 @@ command_send (const char *fingerprint, const char *userid) leave: mime_maker_release (mime); xfree (submission_to); + free_uidinfo_list (uidlist); es_fclose (keyenc); es_fclose (key); xfree (addrspec); diff --git a/tools/gpg-wks-server.c b/tools/gpg-wks-server.c index f7aadba3d..7e3f05017 100644 --- a/tools/gpg-wks-server.c +++ b/tools/gpg-wks-server.c @@ -1105,12 +1105,7 @@ process_new_key (server_ctx_t ctx, estream_t key) err = wks_list_key (key, &ctx->fpr, &ctx->mboxes); if (err) goto leave; - if (!ctx->fpr) - { - log_error ("error parsing key (no fingerprint)\n"); - err = gpg_error (GPG_ERR_NO_PUBKEY); - goto leave; - } + log_assert (ctx->fpr); log_info ("fingerprint: %s\n", ctx->fpr); for (sl = ctx->mboxes; sl; sl = sl->next) { @@ -1358,12 +1353,7 @@ check_and_publish (server_ctx_t ctx, const char *address, const char *nonce) err = wks_list_key (key, &ctx->fpr, &ctx->mboxes); if (err) goto leave; - if (!ctx->fpr) - { - log_error ("error parsing key (no fingerprint)\n"); - err = gpg_error (GPG_ERR_NO_PUBKEY); - goto leave; - } + log_assert (ctx->fpr); log_info ("fingerprint: %s\n", ctx->fpr); for (sl = ctx->mboxes; sl; sl = sl->next) if (sl->mbox) diff --git a/tools/gpg-wks.h b/tools/gpg-wks.h index cb89fd501..ece7add5f 100644 --- a/tools/gpg-wks.h +++ b/tools/gpg-wks.h @@ -87,6 +87,8 @@ 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_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); 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 8fc0a2e5c..889ca36dc 100644 --- a/tools/wks-util.c +++ b/tools/wks-util.c @@ -133,9 +133,9 @@ free_uidinfo_list (uidinfo_list_t list) -/* Helper for wks_list_key. */ +/* Helper for wks_list_key and wks_filter_uid. */ static void -list_key_status_cb (void *opaque, const char *keyword, char *args) +key_status_cb (void *opaque, const char *keyword, char *args) { (void)opaque; @@ -146,7 +146,8 @@ list_key_status_cb (void *opaque, const char *keyword, char *args) /* 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. */ + * is stored at R_FPR and R_MBOXES and an error code is returned. + * R_FPR may be NULL if the fingerprint is not needed. */ gpg_error_t wks_list_key (estream_t key, char **r_fpr, uidinfo_list_t *r_mboxes) { @@ -164,7 +165,8 @@ wks_list_key (estream_t key, char **r_fpr, uidinfo_list_t *r_mboxes) char *fpr = NULL; uidinfo_list_t mboxes = NULL; - *r_fpr = NULL; + if (r_fpr) + *r_fpr = NULL; *r_mboxes = NULL; /* Open a memory stream. */ @@ -200,7 +202,7 @@ wks_list_key (estream_t key, char **r_fpr, uidinfo_list_t *r_mboxes) } err = gnupg_exec_tool_stream (opt.gpg_program, argv, key, NULL, listing, - list_key_status_cb, NULL); + key_status_cb, NULL); if (err) { log_error ("import failed: %s\n", gpg_strerror (err)); @@ -289,8 +291,17 @@ wks_list_key (estream_t key, char **r_fpr, uidinfo_list_t *r_mboxes) goto leave; } - *r_fpr = fpr; - fpr = NULL; + if (!fpr) + { + err = gpg_error (GPG_ERR_NO_PUBKEY); + goto leave; + } + + if (r_fpr) + { + *r_fpr = fpr; + fpr = NULL; + } *r_mboxes = mboxes; mboxes = NULL; @@ -305,6 +316,85 @@ wks_list_key (estream_t key, char **r_fpr, uidinfo_list_t *r_mboxes) } +/* Run gpg as a filter on KEY and write the output to a new stream + * stored at R_NEWKEY. The new key will containn only the user id + * UID. Returns 0 on success. Only one key is expected in KEY. */ +gpg_error_t +wks_filter_uid (estream_t *r_newkey, estream_t key, const char *uid) +{ + gpg_error_t err; + ccparray_t ccp; + const char **argv = NULL; + estream_t newkey; + char *filterexp = NULL; + + *r_newkey = NULL; + + /* Open a memory stream. */ + newkey = es_fopenmem (0, "w+b"); + if (!newkey) + { + err = gpg_error_from_syserror (); + log_error ("error allocating memory buffer: %s\n", gpg_strerror (err)); + return err; + } + + /* Prefix the key with the MIME content type. */ + es_fputs ("Content-Type: application/pgp-keys\n" + "\n", newkey); + + filterexp = es_bsprintf ("keep-uid=uid=%s", uid); + 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) + ccparray_put (&ccp, "--quiet"); + else if (opt.verbose > 1) + ccparray_put (&ccp, "--verbose"); + ccparray_put (&ccp, "--batch"); + ccparray_put (&ccp, "--status-fd=2"); + ccparray_put (&ccp, "--always-trust"); + ccparray_put (&ccp, "--armor"); + ccparray_put (&ccp, "--import-options=import-export"); + ccparray_put (&ccp, "--import-filter"); + ccparray_put (&ccp, filterexp); + ccparray_put (&ccp, "--import"); + + ccparray_put (&ccp, NULL); + argv = ccparray_get (&ccp, NULL); + if (!argv) + { + err = gpg_error_from_syserror (); + goto leave; + } + err = gnupg_exec_tool_stream (opt.gpg_program, argv, key, + NULL, newkey, + key_status_cb, NULL); + if (err) + { + log_error ("import/export failed: %s\n", gpg_strerror (err)); + goto leave; + } + + es_rewind (newkey); + *r_newkey = newkey; + newkey = NULL; + + leave: + xfree (filterexp); + xfree (argv); + es_fclose (newkey); + return err; +} + + /* Helper to write mail to the output(s). */ gpg_error_t wks_send_mime (mime_maker_t mime)