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 <wk@gnupg.org>
(cherry picked from commit e3a1e80d13)
This commit is contained in:
Werner Koch 2018-11-05 20:58:27 +01:00
parent 593895a5e4
commit 66e0bd37ee
No known key found for this signature in database
GPG Key ID: E3FDFF218E45B72B
4 changed files with 230 additions and 68 deletions

View File

@ -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

View File

@ -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;
}

View File

@ -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;

View File

@ -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)