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: 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 \n" "Language-Team: German \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 , 2002. -# !-- psbl.surriel.com rejected (2011-01-11) +# !-- psbl.surriel.com rejected (2011-01-11) # Designated-Translator: none -# +# Dimitris Maroulidis , 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 \n" -"Language-Team: Greek \n" +"PO-Revision-Date: 2017-09-14 21:14+0300\n" +"Last-Translator: Dimitris Maroulidis \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 " Αποτύπωμα υποκλειδιού:" 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; } }