From 66e0bd37ee3dd5ab534b2664493576ef6ad15a08 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Mon, 5 Nov 2018 20:58:27 +0100 Subject: [PATCH] wks: New option --with-colons for gpg-wks-client. * tools/gpg-wks.h (opt): Add field with_colons. * tools/gpg-wks-client.c (oWithColons): New const. (opts, parse_arguments): Add option --with-colons. (main): Change aSupported to take several domains in --with-colons mode. (command_send): Factor policy getting code out to ... (get_policy_and_sa): New function. (command_supported): Make use of new function. -- In addition to this the --create command now also supports a submission address only in the policy file. That means the submission-address file is not anymore required and can be replaced by the policy file. Signed-off-by: Werner Koch (cherry picked from commit e3a1e80d13487c9336640a99b2f6d385d7d6f55c) --- doc/wks.texi | 41 ++++++- tools/gpg-wks-client.c | 254 ++++++++++++++++++++++++++++++----------- tools/gpg-wks.h | 1 + tools/wks-util.c | 2 +- 4 files changed, 230 insertions(+), 68 deletions(-) diff --git a/doc/wks.texi b/doc/wks.texi index b8de29639..89288d041 100644 --- a/doc/wks.texi +++ b/doc/wks.texi @@ -65,7 +65,8 @@ site supports the Web Key Service. The argument is an arbitray address in the to be tested domain. For example @file{foo@@example.net}. The command returns success if the Web Key Service is supported. The operation is silent; to get diagnostic -output use the option @option{--verbose}. +output use the option @option{--verbose}. See option +@option{--with-colons} for a variant of this command. With the @option{--check} command the caller can test whether a key exists for a supplied mail address. The command returns success if a @@ -109,6 +110,44 @@ $(gpgconf --list-dirs libexecdir)/gpg-wks-client --check foo@@example.net Directly send created mails using the @command{sendmail} command. Requires installation of that command. +@item --with-colons +@opindex with-colons +This option has currently only an effect on the @option{--supported} +command. If it is used all arguimenst on the command line are taken +as domain names and tested for WKD support. The output format is one +line per domain with colon delimited fields. The currently specified +fields are (future versions may specify additional fields): + +@table @asis + + @item 1 - domain + This is the domain name. Although quoting is not required for valid + domain names this field is specified to be quoted in standard C + manner. + + @item 2 - WKD + If the value is true the domain supports the Web Key Directory. + + @item 3 - WKS + If the value is true the domain supports the Web Key Service + protocol to upload keys to the directory. + + @item 4 - error-code + This may contain an gpg-error code to describe certain + failures. Use @samp{gpg-error CODE} to explain the code. + + @item 5 - protocol-version + The minimum protocol version supported by the server. + + @item 6 - auth-submit + The auth-submit flag from the policy file of the server. + + @item 7 - mailbox-only + The mailbox-only flag from the policy file of the server. +@end table + + + @item --output @var{file} @itemx -o @opindex output diff --git a/tools/gpg-wks-client.c b/tools/gpg-wks-client.c index 73945ff30..bf6b119e0 100644 --- a/tools/gpg-wks-client.c +++ b/tools/gpg-wks-client.c @@ -61,6 +61,7 @@ enum cmd_and_opt_values oSend, oFakeSubmissionAddr, oStatusFD, + oWithColons, oDummy }; @@ -90,6 +91,7 @@ static ARGPARSE_OPTS opts[] = { ARGPARSE_s_n (oSend, "send", "send the mail using sendmail"), ARGPARSE_s_s (oOutput, "output", "|FILE|write the mail to FILE"), ARGPARSE_s_i (oStatusFD, "status-fd", N_("|FD|write status info to this FD")), + ARGPARSE_s_n (oWithColons, "with-colons", "@"), ARGPARSE_s_s (oFakeSubmissionAddr, "fake-submission-addr", "@"), @@ -204,6 +206,9 @@ parse_arguments (ARGPARSE_ARGS *pargs, ARGPARSE_OPTS *popts) case oStatusFD: wks_set_status_fd (translate_sys2libc_fd_int (pargs->r.ret_int, 1)); break; + case oWithColons: + opt.with_colons = 1; + break; case aSupported: case aCreate: @@ -271,11 +276,20 @@ main (int argc, char **argv) switch (cmd) { case aSupported: - if (argc != 1) - wrong_args ("--supported USER-ID"); - err = command_supported (argv[0]); - if (err && gpg_err_code (err) != GPG_ERR_FALSE) - log_error ("checking support failed: %s\n", gpg_strerror (err)); + if (opt.with_colons) + { + for (; argc; argc--, argv++) + command_supported (*argv); + err = 0; + } + else + { + if (argc != 1) + wrong_args ("--supported DOMAIN"); + err = command_supported (argv[0]); + if (err && gpg_err_code (err) != GPG_ERR_FALSE) + log_error ("checking support failed: %s\n", gpg_strerror (err)); + } break; case aCreate: @@ -471,6 +485,134 @@ decrypt_stream (estream_t *r_output, struct decrypt_stream_parm_s *decinfo, } +/* Return the submission address for the address or just the domain in + * ADDRSPEC. The submission address is stored as a malloced string at + * R_SUBMISSION_ADDRESS. At R_POLICY the policy flags of the domain + * are stored. The caller needs to free them with wks_free_policy. + * The function returns an error code on failure to find a submission + * address or policy file. Note: The function may store NULL at + * R_SUBMISSION_ADDRESS but return success to indicate that the web + * key directory is supported but not the web key service. As per WKD + * specs a policy file is always required and will thus be return on + * success. */ +static gpg_error_t +get_policy_and_sa (const char *addrspec, int silent, + policy_flags_t *r_policy, char **r_submission_address) +{ + gpg_error_t err; + estream_t mbuf = NULL; + const char *domain; + const char *s; + policy_flags_t policy = NULL; + char *submission_to = NULL; + + *r_submission_address = NULL; + *r_policy = NULL; + + domain = strchr (addrspec, '@'); + if (domain) + domain++; + + if (opt.with_colons) + { + s = domain? domain : addrspec; + es_write_sanitized (es_stdout, s, strlen (s), ":", NULL); + es_putc (':', es_stdout); + } + + /* We first try to get the submission address from the policy file + * (this is the new method). If both are available we check that + * they match and print a warning if not. In the latter case we + * keep on using the one from the submission-address file. */ + err = wkd_get_policy_flags (addrspec, &mbuf); + if (err && gpg_err_code (err) != GPG_ERR_NO_DATA + && gpg_err_code (err) != GPG_ERR_NO_NAME) + { + if (!opt.with_colons) + log_error ("error reading policy flags for '%s': %s\n", + domain, gpg_strerror (err)); + goto leave; + } + if (!mbuf) + { + if (!opt.with_colons) + log_error ("provider for '%s' does NOT support the Web Key Directory\n", + addrspec); + err = gpg_error (GPG_ERR_FALSE); + goto leave; + } + + policy = xtrycalloc (1, sizeof *policy); + if (!policy) + err = gpg_error_from_syserror (); + else + err = wks_parse_policy (policy, mbuf, 1); + es_fclose (mbuf); + mbuf = NULL; + if (err) + goto leave; + + err = wkd_get_submission_address (addrspec, &submission_to); + if (err && !policy->submission_address) + { + if (!silent && !opt.with_colons) + log_error (_("error looking up submission address for domain '%s'" + ": %s\n"), domain, gpg_strerror (err)); + if (!silent && gpg_err_code (err) == GPG_ERR_NO_DATA && !opt.with_colons) + log_error (_("this domain probably doesn't support WKS.\n")); + goto leave; + } + + if (submission_to && policy->submission_address + && ascii_strcasecmp (submission_to, policy->submission_address)) + log_info ("Warning: different submission addresses (sa=%s, po=%s)\n", + submission_to, policy->submission_address); + + if (!submission_to && policy->submission_address) + { + submission_to = xtrystrdup (policy->submission_address); + if (!submission_to) + { + err = gpg_error_from_syserror (); + goto leave; + } + } + + leave: + *r_submission_address = submission_to; + submission_to = NULL; + *r_policy = policy; + policy = NULL; + + if (opt.with_colons) + { + if (*r_policy && !*r_submission_address) + es_fprintf (es_stdout, "1:0::"); + else if (*r_policy && *r_submission_address) + es_fprintf (es_stdout, "1:1::"); + else if (err && !(gpg_err_code (err) == GPG_ERR_FALSE + || gpg_err_code (err) == GPG_ERR_NO_DATA + || gpg_err_code (err) == GPG_ERR_UNKNOWN_HOST)) + es_fprintf (es_stdout, "0:0:%d:", err); + else + es_fprintf (es_stdout, "0:0::"); + if (*r_policy) + { + es_fprintf (es_stdout, "%u:%u:%u:", + (*r_policy)->protocol_version, + (*r_policy)->auth_submit, + (*r_policy)->mailbox_only); + } + es_putc ('\n', es_stdout); + } + + xfree (submission_to); + wks_free_policy (policy); + xfree (policy); + es_fclose (mbuf); + return err; +} + /* Check whether the provider supports the WKS protocol. */ @@ -480,6 +622,7 @@ command_supported (char *userid) gpg_error_t err; char *addrspec = NULL; char *submission_to = NULL; + policy_flags_t policy = NULL; if (!strchr (userid, '@')) { @@ -497,24 +640,41 @@ command_supported (char *userid) } /* Get the submission address. */ - err = wkd_get_submission_address (addrspec, &submission_to); - if (err) + err = get_policy_and_sa (addrspec, 1, &policy, &submission_to); + if (err || !submission_to) { - if (gpg_err_code (err) == GPG_ERR_NO_DATA - || gpg_err_code (err) == GPG_ERR_UNKNOWN_HOST) + if (!submission_to + || gpg_err_code (err) == GPG_ERR_FALSE + || gpg_err_code (err) == GPG_ERR_NO_DATA + || gpg_err_code (err) == GPG_ERR_UNKNOWN_HOST + ) { - if (opt.verbose) - log_info ("provider for '%s' does NOT support WKS (%s)\n", - addrspec, gpg_strerror (err)); + /* FALSE is returned if we already figured out that even the + * Web Key Directory is not supported and thus printed an + * error message. */ + if (opt.verbose && gpg_err_code (err) != GPG_ERR_FALSE + && !opt.with_colons) + { + if (gpg_err_code (err) == GPG_ERR_NO_DATA) + log_info ("provider for '%s' does NOT support WKS\n", + addrspec); + else + log_info ("provider for '%s' does NOT support WKS (%s)\n", + addrspec, gpg_strerror (err)); + } err = gpg_error (GPG_ERR_FALSE); - log_inc_errorcount (); + if (!opt.with_colons) + log_inc_errorcount (); } goto leave; } - if (opt.verbose) + + if (opt.verbose && !opt.with_colons) log_info ("provider for '%s' supports WKS\n", addrspec); leave: + wks_free_policy (policy); + xfree (policy); xfree (submission_to); xfree (addrspec); return err; @@ -628,7 +788,7 @@ command_send (const char *fingerprint, const char *userid) estream_t keyenc = NULL; char *submission_to = NULL; mime_maker_t mime = NULL; - struct policy_flags_s policy; + policy_flags_t policy = NULL; int no_encrypt = 0; int posteo_hack = 0; const char *domain; @@ -636,8 +796,6 @@ command_send (const char *fingerprint, const char *userid) uidinfo_list_t uid, thisuid; time_t thistime; - memset (&policy, 0, sizeof policy); - if (classify_user_id (fingerprint, &desc, 1) || !(desc.mode == KEYDB_SEARCH_MODE_FPR || desc.mode == KEYDB_SEARCH_MODE_FPR20)) @@ -665,62 +823,26 @@ command_send (const char *fingerprint, const char *userid) /* Get the submission address. */ if (fake_submission_addr) { + policy = xcalloc (1, sizeof *policy); submission_to = xstrdup (fake_submission_addr); err = 0; } else { - /* We first try to get the submission address from the policy - * file (this is the new method). If both are available we - * check that they match and print a warning if not. In the - * latter case we keep on using the one from the - * submission-address file. */ - estream_t mbuf; - - err = wkd_get_policy_flags (addrspec, &mbuf); - if (err && gpg_err_code (err) != GPG_ERR_NO_DATA) - { - log_error ("error reading policy flags for '%s': %s\n", - domain, gpg_strerror (err)); - goto leave; - } - if (mbuf) - { - err = wks_parse_policy (&policy, mbuf, 1); - es_fclose (mbuf); - if (err) - goto leave; - } - - err = wkd_get_submission_address (addrspec, &submission_to); - if (err && !policy.submission_address) - { - 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; - } - - if (submission_to && policy.submission_address - && ascii_strcasecmp (submission_to, policy.submission_address)) - log_info ("Warning: different submission addresses (sa=%s, po=%s)\n", - submission_to, policy.submission_address); - + err = get_policy_and_sa (addrspec, 0, &policy, &submission_to); + if (err) + goto leave; if (!submission_to) { - submission_to = xtrystrdup (policy.submission_address); - if (!submission_to) - { - err = gpg_error_from_syserror (); - goto leave; - } + log_error (_("this domain probably doesn't support WKS.\n")); + err = gpg_error (GPG_ERR_NO_DATA); + goto leave; } } log_info ("submitting request to '%s'\n", submission_to); - if (policy.auth_submit) + if (policy->auth_submit) log_info ("no confirmation required for '%s'\n", addrspec); /* In case the key has several uids with the same addr-spec we will @@ -738,8 +860,7 @@ command_send (const char *fingerprint, const char *userid) { if (!uid->mbox) continue; /* Should not happen anyway. */ - if (policy.mailbox_only - && ascii_strcasecmp (uid->uid, uid->mbox)) + if (policy->mailbox_only && ascii_strcasecmp (uid->uid, uid->mbox)) continue; /* UID has more than just the mailbox. */ if (uid->created > thistime) { @@ -770,7 +891,7 @@ command_send (const char *fingerprint, const char *userid) key = newkey; } - if (policy.mailbox_only + if (policy->mailbox_only && (!thisuid->mbox || ascii_strcasecmp (thisuid->uid, thisuid->mbox))) { log_info ("Warning: policy requires 'mailbox-only'" @@ -791,7 +912,7 @@ command_send (const char *fingerprint, const char *userid) /* Hack to support posteo but let them disable this by setting the * new policy-version flag. */ - if (policy.protocol_version < 3 + if (policy->protocol_version < 3 && !ascii_strcasecmp (domain, "posteo.de")) { log_info ("Warning: Using draft-1 method for domain '%s'\n", domain); @@ -908,7 +1029,8 @@ command_send (const char *fingerprint, const char *userid) free_uidinfo_list (uidlist); es_fclose (keyenc); es_fclose (key); - wks_free_policy (&policy); + wks_free_policy (policy); + xfree (policy); xfree (addrspec); return err; } diff --git a/tools/gpg-wks.h b/tools/gpg-wks.h index 1b91b6504..fba73f085 100644 --- a/tools/gpg-wks.h +++ b/tools/gpg-wks.h @@ -36,6 +36,7 @@ struct unsigned int debug; int quiet; int use_sendmail; + int with_colons; const char *output; const char *gpg_program; const char *directory; diff --git a/tools/wks-util.c b/tools/wks-util.c index 3fd824c1a..1bcdf7885 100644 --- a/tools/wks-util.c +++ b/tools/wks-util.c @@ -556,7 +556,7 @@ wks_send_mime (mime_maker_t mime) /* Parse the policy flags by reading them from STREAM and storing them - * into FLAGS. If IGNORE_UNKNOWN is iset unknown keywords are + * into FLAGS. If IGNORE_UNKNOWN is set unknown keywords are * ignored. */ gpg_error_t wks_parse_policy (policy_flags_t flags, estream_t stream, int ignore_unknown)