Merge branch 'STABLE-BRANCH-2-2' into master
authorWerner Koch <wk@gnupg.org>
Tue, 26 Sep 2017 10:00:03 +0000 (12:00 +0200)
committerWerner Koch <wk@gnupg.org>
Tue, 26 Sep 2017 10:00:03 +0000 (12:00 +0200)
--

Signed-off-by: Werner Koch <wk@gnupg.org>
Conflicts:
NEWS - include release info from 2.2.1
configure.ac - keep master.

15 files changed:
NEWS
dirmngr/certcache.c
dirmngr/certcache.h
dirmngr/http-ntbtls.c
doc/wks.texi
g10/gpgv.c
g10/keygen.c
po/de.po
po/el.po
tools/gpg-wks-client.c
tools/gpg-wks-server.c
tools/gpg-wks.h
tools/mime-maker.c
tools/mime-maker.h
tools/wks-util.c

diff --git a/NEWS b/NEWS
index 37ebd3f..6cf076d 100644 (file)
--- 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)
 -------------------------------------------------
index b4e5381..56629fd 100644 (file)
@@ -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)
index 92529bf..8d64583 100644 (file)
@@ -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);
index 250db55..ea66a4d 100644 (file)
@@ -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))
index f17497f..55dfee6 100644 (file)
@@ -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
index fb274b3..c43067d 100644 (file)
@@ -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;
index 048a391..e959ee9 100644 (file)
@@ -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;
        }
index 8174b34..2f82d99 100644 (file)
--- 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: translations@gnupg.org\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 <wk@gnupg.org>\n"
 "Language-Team: German <de@li.org>\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 ""
index dd690a4..a57c8b8 100644 (file)
--- 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 <madf@hellug.gr>, 2002.
-#                      !-- psbl.surriel.com rejected (2011-01-11)
+# !-- psbl.surriel.com rejected (2011-01-11)
 # Designated-Translator: none
-#
+# Dimitris Maroulidis <dmaroulidis@dimitrismaroulidis.com>, 2017.
 msgid ""
 msgstr ""
 "Project-Id-Version: gnupg-1.1.92\n"
 "Report-Msgid-Bugs-To: translations@gnupg.org\n"
-"PO-Revision-Date: 2003-06-27 12:00+0200\n"
-"Last-Translator: Dokianakis Theofanis <madf@hellug.gr>\n"
-"Language-Team: Greek <nls@tux.hellug.gr>\n"
+"PO-Revision-Date: 2017-09-14 21:14+0300\n"
+"Last-Translator: Dimitris Maroulidis <dmaroulidis@dimitrismaroulidis.com>\n"
+"Language-Team: team@gnome.gr\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 "      Αποτύπωμα υποκλειδιού:"
index 594f28a..73a8a1f 100644 (file)
@@ -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;
+}
+
+
 \f
 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);
index 1633a20..7e3f050 100644 (file)
@@ -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;
 }
index f73c183..ece7add 100644 (file)
@@ -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);
index d1241f3..0edc14d 100644 (file)
@@ -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
index f2a76cd..c0ddaea 100644 (file)
@@ -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);
index 46ad5c2..889ca36 100644 (file)
@@ -90,9 +90,52 @@ wks_write_status (int no, const char *format, ...)
 
 
 \f
-/* 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;
+    }
+}
+
+
+\f
+/* 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;
         }
     }