aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWerner Koch <[email protected]>2017-09-26 10:00:03 +0000
committerWerner Koch <[email protected]>2017-09-26 10:00:03 +0000
commitcd2d758f3f9e0007ecd6bae9d2ee0d631773e237 (patch)
tree0e1b9c898d0527969fa953e0f1fc418e610c8da4
parentg10: Select a secret key by checking availability under gpg-agent. (diff)
parentpo: Remove trailing colon from a German pinentry string. (diff)
downloadgnupg-cd2d758f3f9e0007ecd6bae9d2ee0d631773e237.tar.gz
gnupg-cd2d758f3f9e0007ecd6bae9d2ee0d631773e237.zip
Merge branch 'STABLE-BRANCH-2-2' into master
-- Signed-off-by: Werner Koch <[email protected]> Conflicts: NEWS - include release info from 2.2.1 configure.ac - keep master.
-rw-r--r--NEWS27
-rw-r--r--dirmngr/certcache.c18
-rw-r--r--dirmngr/certcache.h3
-rw-r--r--dirmngr/http-ntbtls.c6
-rw-r--r--doc/wks.texi4
-rw-r--r--g10/gpgv.c1
-rw-r--r--g10/keygen.c9
-rw-r--r--po/de.po4
-rw-r--r--po/el.po15
-rw-r--r--tools/gpg-wks-client.c283
-rw-r--r--tools/gpg-wks-server.c50
-rw-r--r--tools/gpg-wks.h18
-rw-r--r--tools/mime-maker.c13
-rw-r--r--tools/mime-maker.h2
-rw-r--r--tools/wks-util.c174
15 files changed, 513 insertions, 114 deletions
diff --git a/NEWS b/NEWS
index 37ebd3f9c..6cf076d74 100644
--- a/NEWS
+++ b/NEWS
@@ -1,10 +1,29 @@
Noteworthy changes in version 2.3.0 (unreleased)
------------------------------------------------
+ Changes also found in 2.2.1:
- * Release dates of 2.2.x versions:
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- Version 2.2.1 (unreleased)
+ * gpg: Fix formatting of the user id in batch mode key generation
+ if only "name-email" is given.
+
+ * gpgv: Fix annoying "not suitable for" warnings.
+
+ * wks: Convey only the newest user id to the provider. This is the
+ case if different names are used with the same addr-spec.
+
+ * wks: Create a complying user id for provider policy mailbox-only.
+
+ * wks: Add workaround for posteo.de.
+
+ * scd: Fix the use of large ECC keys with an OpenPGP card.
+
+ * dirmngr: Use system provided root certificates if no specific HKP
+ certificates are configured. If build with GNUTLS, this was
+ already the case.
+
+ Release dates of 2.2.x versions:
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ Version 2.2.1 (2017-09-19)
Noteworthy changes in version 2.2.0 (2017-08-28)
@@ -18,6 +37,8 @@ Noteworthy changes in version 2.2.0 (2017-08-28)
* Fixed a few minor bugs.
+ See-also: gnupg-announce/2017q3/000413.html
+
Noteworthy changes in version 2.1.23 (2017-08-09)
-------------------------------------------------
diff --git a/dirmngr/certcache.c b/dirmngr/certcache.c
index b4e538131..56629fdda 100644
--- a/dirmngr/certcache.c
+++ b/dirmngr/certcache.c
@@ -94,6 +94,10 @@ static int initialization_done;
/* Total number of non-permanent certificates. */
static unsigned int total_nonperm_certificates;
+/* For each cert class the corresponding bit is set if at least one
+ * certificate of that class is loaded permanetly. */
+static unsigned int any_cert_of_class;
+
#ifdef HAVE_W32_SYSTEM
/* We load some functions dynamically. Provide typedefs for tehse
@@ -343,7 +347,9 @@ put_cert (ksba_cert_t cert, int permanent, unsigned int trustclass,
ci->permanent = !!permanent;
ci->trustclasses = trustclass;
- if (!permanent)
+ if (permanent)
+ any_cert_of_class |= trustclass;
+ else
total_nonperm_certificates++;
return 0;
@@ -758,6 +764,7 @@ cert_cache_deinit (int full)
}
total_nonperm_certificates = 0;
+ any_cert_of_class = 0;
initialization_done = 0;
release_cache_lock ();
}
@@ -814,6 +821,15 @@ cert_cache_print_stats (void)
}
+/* Return true if any cert of a class in MASK is permanently
+ * loaded. */
+int
+cert_cache_any_in_class (unsigned int mask)
+{
+ return !!(any_cert_of_class & mask);
+}
+
+
/* Put CERT into the certificate cache. */
gpg_error_t
cache_cert (ksba_cert_t cert)
diff --git a/dirmngr/certcache.h b/dirmngr/certcache.h
index 92529bf11..8d645836d 100644
--- a/dirmngr/certcache.h
+++ b/dirmngr/certcache.h
@@ -39,6 +39,9 @@ void cert_cache_deinit (int full);
/* Print some statistics to the log file. */
void cert_cache_print_stats (void);
+/* Return true if any cert of a class in MASK is permanently loaded. */
+int cert_cache_any_in_class (unsigned int mask);
+
/* Compute the fingerprint of the certificate CERT and put it into
the 20 bytes large buffer DIGEST. Return address of this buffer. */
unsigned char *cert_compute_fpr (ksba_cert_t cert, unsigned char *digest);
diff --git a/dirmngr/http-ntbtls.c b/dirmngr/http-ntbtls.c
index 250db556c..ea66a4d73 100644
--- a/dirmngr/http-ntbtls.c
+++ b/dirmngr/http-ntbtls.c
@@ -91,6 +91,12 @@ gnupg_http_tls_verify_cb (void *opaque,
validate_flags |= VALIDATE_FLAG_TRUST_HKP;
if ((http_flags & HTTP_FLAG_TRUST_SYS))
validate_flags |= VALIDATE_FLAG_TRUST_SYSTEM;
+
+ /* If HKP trust is requested and there are no HKP certificates
+ * configured, also try thye standard system certificates. */
+ if ((validate_flags & VALIDATE_FLAG_TRUST_HKP)
+ && !cert_cache_any_in_class (CERTTRUST_CLASS_HKP))
+ validate_flags |= VALIDATE_FLAG_TRUST_SYSTEM;
}
if ((http_flags & HTTP_FLAG_NO_CRL))
diff --git a/doc/wks.texi b/doc/wks.texi
index f17497f9b..55dfee6d5 100644
--- a/doc/wks.texi
+++ b/doc/wks.texi
@@ -78,7 +78,9 @@ the command is a properly formatted mail with all standard headers.
This mail can be fed to @command{sendmail(8)} or any other tool to
actually send that mail. If @command{sendmail(8)} is installed the
option @option{--send} can be used to directly send the created
-request.
+request. If the provider request a 'mailbox-only' user id and no such
+user id is found, @command{gpg-wks-client} will try an additional user
+id.
The @option{--receive} and @option{--read} commands are used to
process confirmation mails as send from the service provider. The
diff --git a/g10/gpgv.c b/g10/gpgv.c
index fb274b337..c43067dfd 100644
--- a/g10/gpgv.c
+++ b/g10/gpgv.c
@@ -202,6 +202,7 @@ main( int argc, char **argv )
dotlock_disable ();
gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
additional_weak_digest("MD5");
+ gnupg_initialize_compliance (GNUPG_MODULE_NAME_GPG);
pargs.argc = &argc;
pargs.argv = &argv;
diff --git a/g10/keygen.c b/g10/keygen.c
index 048a391c3..e959ee901 100644
--- a/g10/keygen.c
+++ b/g10/keygen.c
@@ -3529,7 +3529,14 @@ proc_parameter_file (ctrl_t ctrl, struct para_data_s *para, const char *fname,
if( s2 )
p = stpcpy(stpcpy(stpcpy(p," ("), s2 ),")");
if( s3 )
- p = stpcpy(stpcpy(stpcpy(p," <"), s3 ),">");
+ {
+ /* If we have only the email part, do not add the space
+ * and the angle brackets. */
+ if (*r->u.value)
+ p = stpcpy(stpcpy(stpcpy(p," <"), s3 ),">");
+ else
+ p = stpcpy (p, s3);
+ }
append_to_parameter (para, r);
have_user_id=1;
}
diff --git a/po/de.po b/po/de.po
index 8174b3416..2f82d9910 100644
--- a/po/de.po
+++ b/po/de.po
@@ -9,7 +9,7 @@ msgid ""
msgstr ""
"Project-Id-Version: gnupg-2.1.0\n"
"Report-Msgid-Bugs-To: [email protected]\n"
-"PO-Revision-Date: 2017-08-09 12:49+0200\n"
+"PO-Revision-Date: 2017-09-26 11:51+0200\n"
"Last-Translator: Werner Koch <[email protected]>\n"
"Language-Team: German <[email protected]>\n"
"Language: de\n"
@@ -194,7 +194,7 @@ msgid "failed to create stream from socket: %s\n"
msgstr "Das Erzeugen eines Datenstroms aus dem Socket schlug fehl: %s\n"
msgid "Please insert the card with serial number"
-msgstr "Die legen Sie die Karte mit der folgenden Seriennummer ein:"
+msgstr "Bitte legen Sie die Karte mit der folgenden Seriennummer ein"
msgid "Please remove the current card and insert the one with serial number"
msgstr ""
diff --git a/po/el.po b/po/el.po
index dd690a46e..a57c8b8dd 100644
--- a/po/el.po
+++ b/po/el.po
@@ -1,20 +1,21 @@
# Greek Translation of GnuPG.
# Copyright (C) 2002 Free Software Foundation, Inc.
# Dokianakis Theofanis <[email protected]>, 2002.
-# !-- psbl.surriel.com rejected (2011-01-11)
+# !-- psbl.surriel.com rejected (2011-01-11)
# Designated-Translator: none
-#
+# Dimitris Maroulidis <[email protected]>, 2017.
msgid ""
msgstr ""
"Project-Id-Version: gnupg-1.1.92\n"
"Report-Msgid-Bugs-To: [email protected]\n"
-"PO-Revision-Date: 2003-06-27 12:00+0200\n"
-"Last-Translator: Dokianakis Theofanis <[email protected]>\n"
-"Language-Team: Greek <[email protected]>\n"
+"PO-Revision-Date: 2017-09-14 21:14+0300\n"
+"Last-Translator: Dimitris Maroulidis <[email protected]>\n"
+"Language-Team: [email protected]\n"
"Language: el\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#, fuzzy, c-format
msgid "failed to acquire the pinentry lock: %s\n"
@@ -4423,7 +4424,7 @@ msgid "Keyring"
msgstr "Κλειδοθήκη"
msgid "Primary key fingerprint:"
-msgstr "Αποτύπωμα πρωτεύων κλειδιού:"
+msgstr "Αποτύπωμα πρωτεύοντος κλειδιού:"
msgid " Subkey fingerprint:"
msgstr " Αποτύπωμα υποκλειδιού:"
@@ -4431,7 +4432,7 @@ msgstr " Αποτύπωμα υποκλειδιού:"
#. TRANSLATORS: this should fit into 24 bytes so that the
#. * fingerprint data is properly aligned with the user ID
msgid " Primary key fingerprint:"
-msgstr " Αποτύπωμα πρωτεύων κλειδιού:"
+msgstr " Αποτύπωμα πρωτ. κλειδιού:"
msgid " Subkey fingerprint:"
msgstr " Αποτύπωμα υποκλειδιού:"
diff --git a/tools/gpg-wks-client.c b/tools/gpg-wks-client.c
index 594f28a7c..73a8a1f43 100644
--- a/tools/gpg-wks-client.c
+++ b/tools/gpg-wks-client.c
@@ -119,7 +119,7 @@ const char *fake_submission_addr;
static void wrong_args (const char *text) GPGRT_ATTR_NORETURN;
static gpg_error_t command_supported (char *userid);
static gpg_error_t command_check (char *userid);
-static gpg_error_t command_send (const char *fingerprint, char *userid);
+static gpg_error_t command_send (const char *fingerprint, const char *userid);
static gpg_error_t encrypt_response (estream_t *r_output, estream_t input,
const char *addrspec,
const char *fingerprint);
@@ -348,13 +348,13 @@ get_key_status_cb (void *opaque, const char *keyword, char *args)
/* Get a key by fingerprint from gpg's keyring and make sure that the
- * mail address ADDRSPEC is included in the key. The key is returned
- * as a new memory stream at R_KEY.
- *
- * Fixme: After we have implemented import and export filters for gpg
- * this function shall only return a key with just this user id. */
+ * 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. */
static gpg_error_t
-get_key (estream_t *r_key, const char *fingerprint, const char *addrspec)
+get_key (estream_t *r_key, const char *fingerprint, const char *addrspec,
+ int exact)
{
gpg_error_t err;
ccparray_t ccp;
@@ -379,7 +379,7 @@ get_key (estream_t *r_key, const char *fingerprint, const char *addrspec)
es_fputs ("Content-Type: application/pgp-keys\n"
"\n", key);
- filterexp = es_bsprintf ("keep-uid=mbox = %s", addrspec);
+ filterexp = es_bsprintf ("keep-uid=%s=%s", exact? "uid":"mbox", addrspec);
if (!filterexp)
{
err = gpg_error_from_syserror ();
@@ -438,6 +438,49 @@ get_key (estream_t *r_key, const char *fingerprint, const char *addrspec)
}
+/* Add the user id UID to the key identified by FINGERPRINT. */
+static gpg_error_t
+add_user_id (const char *fingerprint, const char *uid)
+{
+ gpg_error_t err;
+ ccparray_t ccp;
+ const char **argv = NULL;
+
+ 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, "--always-trust");
+ ccparray_put (&ccp, "--quick-add-uid");
+ ccparray_put (&ccp, fingerprint);
+ ccparray_put (&ccp, uid);
+
+ 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, NULL,
+ NULL, NULL,
+ NULL, NULL);
+ if (err)
+ {
+ log_error ("adding user id failed: %s\n", gpg_strerror (err));
+ goto leave;
+ }
+
+ leave:
+ xfree (argv);
+ return err;
+}
+
+
struct decrypt_stream_parm_s
{
@@ -600,8 +643,8 @@ command_check (char *userid)
char *addrspec = NULL;
estream_t key = NULL;
char *fpr = NULL;
- strlist_t mboxes = NULL;
- strlist_t sl;
+ uidinfo_list_t mboxes = NULL;
+ uidinfo_list_t sl;
int found = 0;
addrspec = mailbox_from_userid (userid);
@@ -647,10 +690,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;
}
@@ -660,10 +702,15 @@ command_check (char *userid)
for (sl = mboxes; sl; sl = sl->next)
{
- if (!strcmp (sl->d, addrspec))
+ if (sl->mbox && !strcmp (sl->mbox, addrspec))
found = 1;
if (opt.verbose)
- log_info (" addr-spec: %s\n", sl->d);
+ {
+ log_info (" user-id: %s\n", sl->uid);
+ log_info (" created: %s\n", asctimestamp (sl->created));
+ if (sl->mbox)
+ log_info (" addr-spec: %s\n", sl->mbox);
+ }
}
if (!found)
{
@@ -674,7 +721,7 @@ command_check (char *userid)
leave:
xfree (fpr);
- free_strlist (mboxes);
+ free_uidinfo_list (mboxes);
es_fclose (key);
xfree (addrspec);
return err;
@@ -685,7 +732,7 @@ command_check (char *userid)
/* Locate the key by fingerprint and userid and send a publication
* request. */
static gpg_error_t
-command_send (const char *fingerprint, char *userid)
+command_send (const char *fingerprint, const char *userid)
{
gpg_error_t err;
KEYDB_SEARCH_DESC desc;
@@ -695,6 +742,12 @@ command_send (const char *fingerprint, char *userid)
char *submission_to = NULL;
mime_maker_t mime = NULL;
struct policy_flags_s policy;
+ 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);
@@ -706,6 +759,7 @@ command_send (const char *fingerprint, char *userid)
err = gpg_error (GPG_ERR_INV_NAME);
goto leave;
}
+
addrspec = mailbox_from_userid (userid);
if (!addrspec)
{
@@ -713,10 +767,14 @@ command_send (const char *fingerprint, char *userid)
err = gpg_error (GPG_ERR_INV_USER_ID);
goto leave;
}
- err = get_key (&key, fingerprint, addrspec);
+ err = get_key (&key, fingerprint, addrspec, 0);
if (err)
goto leave;
+ domain = strchr (addrspec, '@');
+ log_assert (domain);
+ domain++;
+
/* Get the submission address. */
if (fake_submission_addr)
{
@@ -727,11 +785,8 @@ command_send (const char *fingerprint, char *userid)
err = wkd_get_submission_address (addrspec, &submission_to);
if (err)
{
- char *domain = strchr (addrspec, '@');
- if (domain)
- domain = domain + 1;
- log_error (_("looking up WKS submission address for %s: %s\n"),
- domain ? domain : addrspec, gpg_strerror (err));
+ log_error (_("error looking up submission address for domain '%s': %s\n"),
+ domain, gpg_strerror (err));
if (gpg_err_code (err) == GPG_ERR_NO_DATA)
log_error (_("this domain probably doesn't support WKS.\n"));
goto leave;
@@ -762,14 +817,92 @@ command_send (const char *fingerprint, char *userid)
if (policy.auth_submit)
log_info ("no confirmation required for '%s'\n", addrspec);
- /* Encrypt the key part. */
- es_rewind (key);
- err = encrypt_response (&keyenc, key, submission_to, fingerprint);
+ /* 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)
- goto leave;
- es_fclose (key);
- key = NULL;
+ {
+ 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 (policy.mailbox_only
+ && ascii_strcasecmp (uid->uid, uid->mbox))
+ continue; /* UID has more than just the mailbox. */
+ if (uid->created > thistime)
+ {
+ thistime = uid->created;
+ thisuid = uid;
+ }
+ }
+ if (!thisuid)
+ thisuid = uidlist; /* 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
+ && (!thisuid->mbox || ascii_strcasecmp (thisuid->uid, thisuid->mbox)))
+ {
+ log_info ("Warning: policy requires 'mailbox-only'"
+ " - adding user id '%s'\n", addrspec);
+ err = add_user_id (fingerprint, addrspec);
+ if (err)
+ goto leave;
+
+ /* Need to get the key again. This time we request filtering
+ * for the full user id, so that we do not need check and filter
+ * the key again. */
+ es_fclose (key);
+ key = NULL;
+ err = get_key (&key, fingerprint, addrspec, 1);
+ if (err)
+ goto leave;
+ }
+
+ /* Hack to support posteo but let them disable this by setting the
+ * new policy-version flag. */
+ if (policy.protocol_version < 3
+ && !ascii_strcasecmp (domain, "posteo.de"))
+ {
+ log_info ("Warning: Using draft-1 method for domain '%s'\n", domain);
+ no_encrypt = 1;
+ posteo_hack = 1;
+ }
+
+ /* Encrypt the key part. */
+ if (!no_encrypt)
+ {
+ es_rewind (key);
+ err = encrypt_response (&keyenc, key, submission_to, fingerprint);
+ if (err)
+ goto leave;
+ es_fclose (key);
+ key = NULL;
+ }
/* Send the key. */
err = mime_maker_new (&mime, NULL);
@@ -787,40 +920,86 @@ command_send (const char *fingerprint, char *userid)
/* Tell server which draft we support. */
err = mime_maker_add_header (mime, "Wks-Draft-Version",
- STR2(WKS_DRAFT_VERSION));
+ STR2(WKS_DRAFT_VERSION));
if (err)
goto leave;
- err = mime_maker_add_header (mime, "Content-Type",
- "multipart/encrypted; "
- "protocol=\"application/pgp-encrypted\"");
- if (err)
- goto leave;
- err = mime_maker_add_container (mime);
- if (err)
- goto leave;
+ if (no_encrypt)
+ {
+ void *data;
+ size_t datalen, n;
- err = mime_maker_add_header (mime, "Content-Type",
- "application/pgp-encrypted");
- if (err)
- goto leave;
- err = mime_maker_add_body (mime, "Version: 1\n");
- if (err)
- goto leave;
- err = mime_maker_add_header (mime, "Content-Type",
- "application/octet-stream");
- if (err)
- goto leave;
+ if (posteo_hack)
+ {
+ /* Needs a multipart/mixed with one(!) attachment. It does
+ * not grok a non-multipart mail. */
+ err = mime_maker_add_header (mime, "Content-Type", "multipart/mixed");
+ if (err)
+ goto leave;
+ err = mime_maker_add_container (mime);
+ if (err)
+ goto leave;
+ }
- err = mime_maker_add_stream (mime, &keyenc);
- if (err)
- goto leave;
+ err = mime_maker_add_header (mime, "Content-type",
+ "application/pgp-keys");
+ if (err)
+ goto leave;
+
+ if (es_fclose_snatch (key, &data, &datalen))
+ {
+ err = gpg_error_from_syserror ();
+ 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);
+ xfree (data);
+ if (err)
+ goto leave;
+ }
+ else
+ {
+ err = mime_maker_add_header (mime, "Content-Type",
+ "multipart/encrypted; "
+ "protocol=\"application/pgp-encrypted\"");
+ if (err)
+ goto leave;
+ err = mime_maker_add_container (mime);
+ if (err)
+ goto leave;
+
+ err = mime_maker_add_header (mime, "Content-Type",
+ "application/pgp-encrypted");
+ if (err)
+ goto leave;
+ err = mime_maker_add_body (mime, "Version: 1\n");
+ if (err)
+ goto leave;
+ err = mime_maker_add_header (mime, "Content-Type",
+ "application/octet-stream");
+ if (err)
+ goto leave;
+
+ err = mime_maker_add_stream (mime, &keyenc);
+ if (err)
+ goto leave;
+ }
err = wks_send_mime (mime);
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 1633a2084..7e3f05017 100644
--- a/tools/gpg-wks-server.c
+++ b/tools/gpg-wks-server.c
@@ -127,7 +127,7 @@ static struct debug_flags_s debug_flags [] =
struct server_ctx_s
{
char *fpr;
- strlist_t mboxes; /* List of addr-specs taken from the UIDs. */
+ uidinfo_list_t mboxes; /* List with addr-specs taken from the UIDs. */
unsigned int draft_version_2:1; /* Client supports the draft 2. */
};
typedef struct server_ctx_s *server_ctx_t;
@@ -1092,7 +1092,7 @@ static gpg_error_t
process_new_key (server_ctx_t ctx, estream_t key)
{
gpg_error_t err;
- strlist_t sl;
+ uidinfo_list_t sl;
const char *s;
char *dname = NULL;
char *nonce = NULL;
@@ -1101,27 +1101,25 @@ process_new_key (server_ctx_t ctx, estream_t key)
/* First figure out the user id from the key. */
xfree (ctx->fpr);
- free_strlist (ctx->mboxes);
+ free_uidinfo_list (ctx->mboxes);
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)
{
- log_info (" addr-spec: %s\n", sl->d);
+ if (sl->mbox)
+ log_info (" addr-spec: %s\n", sl->mbox);
}
/* Walk over all user ids and send confirmation requests for those
* we support. */
for (sl = ctx->mboxes; sl; sl = sl->next)
{
- s = strchr (sl->d, '@');
+ if (!sl->mbox)
+ continue;
+ s = strchr (sl->mbox, '@');
log_assert (s && s[1]);
xfree (dname);
dname = make_filename_try (opt.directory, s+1, NULL);
@@ -1133,26 +1131,26 @@ process_new_key (server_ctx_t ctx, estream_t key)
if (access (dname, W_OK))
{
- log_info ("skipping address '%s': Domain not configured\n", sl->d);
+ log_info ("skipping address '%s': Domain not configured\n", sl->mbox);
continue;
}
- if (get_policy_flags (&policybuf, sl->d))
+ if (get_policy_flags (&policybuf, sl->mbox))
{
- log_info ("skipping address '%s': Bad policy flags\n", sl->d);
+ log_info ("skipping address '%s': Bad policy flags\n", sl->mbox);
continue;
}
if (policybuf.auth_submit)
{
/* Bypass the confirmation stuff and publish the key as is. */
- log_info ("publishing address '%s'\n", sl->d);
+ log_info ("publishing address '%s'\n", sl->mbox);
/* FIXME: We need to make sure that we do this only for the
* address in the mail. */
log_debug ("auth-submit not yet working!\n");
}
else
{
- log_info ("storing address '%s'\n", sl->d);
+ log_info ("storing address '%s'\n", sl->mbox);
xfree (nonce);
xfree (fname);
@@ -1160,7 +1158,7 @@ process_new_key (server_ctx_t ctx, estream_t key)
if (err)
goto leave;
- err = send_confirmation_request (ctx, sl->d, nonce, fname);
+ err = send_confirmation_request (ctx, sl->mbox, nonce, fname);
if (err)
goto leave;
}
@@ -1313,7 +1311,7 @@ check_and_publish (server_ctx_t ctx, const char *address, const char *nonce)
char *hash = NULL;
const char *domain;
const char *s;
- strlist_t sl;
+ uidinfo_list_t sl;
char shaxbuf[32]; /* Used for SHA-1 and SHA-256 */
/* FIXME: There is a bug in name-value.c which adds white space for
@@ -1351,25 +1349,21 @@ check_and_publish (server_ctx_t ctx, const char *address, const char *nonce)
/* We need to get the fingerprint from the key. */
xfree (ctx->fpr);
- free_strlist (ctx->mboxes);
+ free_uidinfo_list (ctx->mboxes);
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)
- log_info (" addr-spec: %s\n", sl->d);
+ if (sl->mbox)
+ log_info (" addr-spec: %s\n", sl->mbox);
/* Check that the key has 'address' as a user id. We use
* case-insensitive matching because the client is expected to
* return the address verbatim. */
for (sl = ctx->mboxes; sl; sl = sl->next)
- if (!strcmp (sl->d, address))
+ if (sl->mbox && !strcmp (sl->mbox, address))
break;
if (!sl)
{
@@ -1565,7 +1559,7 @@ command_receive_cb (void *opaque, const char *mediatype,
}
xfree (ctx.fpr);
- free_strlist (ctx.mboxes);
+ free_uidinfo_list (ctx.mboxes);
return err;
}
diff --git a/tools/gpg-wks.h b/tools/gpg-wks.h
index f73c183e0..ece7add5f 100644
--- a/tools/gpg-wks.h
+++ b/tools/gpg-wks.h
@@ -63,16 +63,32 @@ struct policy_flags_s
unsigned int mailbox_only : 1;
unsigned int dane_only : 1;
unsigned int auth_submit : 1;
+ unsigned int protocol_version; /* The supported WKS_DRAFT_VERION or 0 */
unsigned int max_pending; /* Seconds to wait for a confirmation. */
};
typedef struct policy_flags_s *policy_flags_t;
+/* An object to convey user ids of a key. */
+struct uidinfo_list_s
+{
+ struct uidinfo_list_s *next;
+ time_t created; /* Time the userid was created. */
+ char *mbox; /* NULL or the malloced mailbox from UID. */
+ char uid[1];
+};
+typedef struct uidinfo_list_s *uidinfo_list_t;
+
+
/*-- wks-util.c --*/
void wks_set_status_fd (int fd);
void wks_write_status (int no, const char *format, ...) GPGRT_ATTR_PRINTF(2,3);
-gpg_error_t wks_list_key (estream_t key, char **r_fpr, strlist_t *r_mboxes);
+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/mime-maker.c b/tools/mime-maker.c
index d1241f3f8..0edc14d78 100644
--- a/tools/mime-maker.c
+++ b/tools/mime-maker.c
@@ -478,7 +478,8 @@ add_body (mime_maker_t ctx, const void *data, size_t datalen)
/* Add STRING as body to the mail or the current MIME container. A
- * second call to this function is not allowed.
+ * second call to this function or mime_make_add_body_data is not
+ * allowed.
*
* FIXME: We may want to have an append_body to add more data to a body.
*/
@@ -489,6 +490,16 @@ mime_maker_add_body (mime_maker_t ctx, const char *string)
}
+/* Add (DATA,DATALEN) as body to the mail or the current MIME
+ * container. Note that a second call to this function or to
+ * mime_make_add_body is not allowed. */
+gpg_error_t
+mime_maker_add_body_data (mime_maker_t ctx, const void *data, size_t datalen)
+{
+ return add_body (ctx, data, datalen);
+}
+
+
/* This is the same as mime_maker_add_body but takes a stream as
* argument. As of now the stream is copied to the MIME object but
* eventually we may delay that and read the stream only at the time
diff --git a/tools/mime-maker.h b/tools/mime-maker.h
index f2a76cdb8..c0ddaeaa5 100644
--- a/tools/mime-maker.h
+++ b/tools/mime-maker.h
@@ -34,6 +34,8 @@ void mime_maker_dump_tree (mime_maker_t ctx);
gpg_error_t mime_maker_add_header (mime_maker_t ctx,
const char *name, const char *value);
gpg_error_t mime_maker_add_body (mime_maker_t ctx, const char *string);
+gpg_error_t mime_maker_add_body_data (mime_maker_t ctx,
+ const void *data, size_t datalen);
gpg_error_t mime_maker_add_stream (mime_maker_t ctx, estream_t *stream_addr);
gpg_error_t mime_maker_add_container (mime_maker_t ctx);
gpg_error_t mime_maker_end_container (mime_maker_t ctx);
diff --git a/tools/wks-util.c b/tools/wks-util.c
index 46ad5c27b..889ca36dc 100644
--- a/tools/wks-util.c
+++ b/tools/wks-util.c
@@ -90,9 +90,52 @@ wks_write_status (int no, const char *format, ...)
-/* Helper for wks_list_key. */
+
+/* Append UID to LIST and return the new item. On success LIST is
+ * updated. 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)
+{
+ uidinfo_list_t r, sl;
+
+ sl = xtrymalloc (sizeof *sl + strlen (uid));
+ if (!sl)
+ return NULL;
+
+ strcpy (sl->uid, uid);
+ sl->created = created;
+ sl->mbox = mailbox_from_userid (uid);
+ sl->next = NULL;
+ if (!*list)
+ *list = sl;
+ else
+ {
+ for (r = *list; r->next; r = r->next )
+ ;
+ r->next = sl;
+ }
+ return sl;
+}
+
+
+/* Free the list of uid infos at LIST. */
+void
+free_uidinfo_list (uidinfo_list_t list)
+{
+ while (list)
+ {
+ uidinfo_list_t tmp = list->next;
+ xfree (list->mbox);
+ xfree (list);
+ list = tmp;
+ }
+}
+
+
+
+/* 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;
@@ -103,9 +146,10 @@ 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, strlist_t *r_mboxes)
+wks_list_key (estream_t key, char **r_fpr, uidinfo_list_t *r_mboxes)
{
gpg_error_t err;
ccparray_t ccp;
@@ -118,11 +162,11 @@ wks_list_key (estream_t key, char **r_fpr, strlist_t *r_mboxes)
char **fields = NULL;
int nfields;
int lnr;
- char *mbox = NULL;
char *fpr = NULL;
- strlist_t mboxes = NULL;
+ uidinfo_list_t mboxes = NULL;
- *r_fpr = NULL;
+ if (r_fpr)
+ *r_fpr = NULL;
*r_mboxes = NULL;
/* Open a memory stream. */
@@ -158,7 +202,7 @@ wks_list_key (estream_t key, char **r_fpr, strlist_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));
@@ -232,9 +276,8 @@ wks_list_key (estream_t key, char **r_fpr, strlist_t *r_mboxes)
else if (!strcmp (fields[0], "uid") && nfields > 9)
{
/* Fixme: Unescape fields[9] */
- xfree (mbox);
- mbox = mailbox_from_userid (fields[9]);
- if (mbox && !append_to_strlist_try (&mboxes, mbox))
+ if (!append_to_uidinfo_list (&mboxes, fields[9],
+ parse_timestamp (fields[5], NULL)))
{
err = gpg_error_from_syserror ();
goto leave;
@@ -248,15 +291,23 @@ wks_list_key (estream_t key, char **r_fpr, strlist_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;
leave:
xfree (fpr);
- xfree (mboxes);
- xfree (mbox);
+ free_uidinfo_list (mboxes);
xfree (fields);
es_free (line);
xfree (argv);
@@ -265,6 +316,85 @@ wks_list_key (estream_t key, char **r_fpr, strlist_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)
@@ -316,7 +446,8 @@ wks_parse_policy (policy_flags_t flags, estream_t stream, int ignore_unknown)
TOK_MAILBOX_ONLY,
TOK_DANE_ONLY,
TOK_AUTH_SUBMIT,
- TOK_MAX_PENDING
+ TOK_MAX_PENDING,
+ TOK_PROTOCOL_VERSION
};
static struct {
const char *name;
@@ -325,7 +456,8 @@ wks_parse_policy (policy_flags_t flags, estream_t stream, int ignore_unknown)
{ "mailbox-only", TOK_MAILBOX_ONLY },
{ "dane-only", TOK_DANE_ONLY },
{ "auth-submit", TOK_AUTH_SUBMIT },
- { "max-pending", TOK_MAX_PENDING }
+ { "max-pending", TOK_MAX_PENDING },
+ { "protocol-version", TOK_PROTOCOL_VERSION }
};
gpg_error_t err = 0;
int lnr = 0;
@@ -400,6 +532,14 @@ wks_parse_policy (policy_flags_t flags, estream_t stream, int ignore_unknown)
* and decide whether to allow other units. */
flags->max_pending = atoi (value);
break;
+ case TOK_PROTOCOL_VERSION:
+ if (!value)
+ {
+ err = gpg_error (GPG_ERR_SYNTAX);
+ goto leave;
+ }
+ flags->protocol_version = atoi (value);
+ break;
}
}