1
0
mirror of git://git.gnupg.org/gnupg.git synced 2025-01-03 12:11:33 +01:00

wks: Implement server command --install-key.

* tools/wks-util.c (wks_filter_uid): Add arg 'binary'.
* tools/gpg-wks-server.c (main): Expect 2 args for --install-key.
(write_to_file): New.
(check_and_publish): Factor some code out to ...
(compute_hu_fname): ... new.
(command_install_key): Implement.

Signed-off-by: Werner Koch <wk@gnupg.org>
This commit is contained in:
Werner Koch 2018-02-20 11:45:58 +01:00
parent 1877603761
commit ee474856ec
No known key found for this signature in database
GPG Key ID: E3FDFF218E45B72B
5 changed files with 216 additions and 40 deletions

View File

@ -181,6 +181,7 @@ Display a brief help page and exit.
.RI [ options ] .RI [ options ]
.B \-\-install-key .B \-\-install-key
.I file .I file
.I user-id
.br .br
.B gpg-wks-server .B gpg-wks-server
.RI [ options ] .RI [ options ]
@ -221,14 +222,17 @@ the process returns failure; to suppress the diagnostic, use option
@option{-q}. More than one user-id can be given; see also option @option{-q}. More than one user-id can be given; see also option
@option{with-file}. @option{with-file}.
The command @option{--install-key} manually installs a key into the
WKD. The arguments are a file with the keyblock and the user-id to
install.
The command @option{--remove-key} uninstalls a key from the WKD. The The command @option{--remove-key} uninstalls a key from the WKD. The
process return success in this case; to also print a diagnostic, use process returns success in this case; to also print a diagnostic, use
option @option{-v}. If the key is not installed a diagnostics is option @option{-v}. If the key is not installed a diagnostic is
printed and the process returns failure; to suppress the diagnostic, printed and the process returns failure; to suppress the diagnostic,
use option @option{-q}. use option @option{-q}.
The commands @option{--install-key} and @option{--revoke-key} are not The command @option{--revoke-key} is not yet functional.
yet functional.
@mansect options @mansect options

View File

@ -872,7 +872,7 @@ command_send (const char *fingerprint, const char *userid)
estream_t newkey; estream_t newkey;
es_rewind (key); es_rewind (key);
err = wks_filter_uid (&newkey, key, thisuid->uid); err = wks_filter_uid (&newkey, key, thisuid->uid, 0);
if (err) if (err)
{ {
log_error ("error filtering key: %s\n", gpg_strerror (err)); log_error ("error filtering key: %s\n", gpg_strerror (err));

View File

@ -1,5 +1,5 @@
/* gpg-wks-server.c - A server for the Web Key Service protocols. /* gpg-wks-server.c - A server for the Web Key Service protocols.
* Copyright (C) 2016 Werner Koch * Copyright (C) 2016, 2018 Werner Koch
* Copyright (C) 2016 Bundesamt für Sicherheit in der Informationstechnik * Copyright (C) 2016 Bundesamt für Sicherheit in der Informationstechnik
* *
* This file is part of GnuPG. * This file is part of GnuPG.
@ -20,7 +20,7 @@
/* The Web Key Service I-D defines an update protocol to store a /* The Web Key Service I-D defines an update protocol to store a
* public key in the Web Key Directory. The current specification is * public key in the Web Key Directory. The current specification is
* draft-koch-openpgp-webkey-service-01.txt. * draft-koch-openpgp-webkey-service-05.txt.
*/ */
#include <config.h> #include <config.h>
@ -154,7 +154,7 @@ static gpg_error_t command_receive_cb (void *opaque,
const char *mediatype, estream_t fp, const char *mediatype, estream_t fp,
unsigned int flags); unsigned int flags);
static gpg_error_t command_list_domains (void); static gpg_error_t command_list_domains (void);
static gpg_error_t command_install_key (const char *fname); static gpg_error_t command_install_key (const char *fname, const char *userid);
static gpg_error_t command_remove_key (const char *mailaddr); static gpg_error_t command_remove_key (const char *mailaddr);
static gpg_error_t command_revoke_key (const char *mailaddr); static gpg_error_t command_revoke_key (const char *mailaddr);
static gpg_error_t command_check_key (const char *mailaddr); static gpg_error_t command_check_key (const char *mailaddr);
@ -376,9 +376,9 @@ main (int argc, char **argv)
break; break;
case aInstallKey: case aInstallKey:
if (argc != 1) if (argc != 2)
wrong_args ("--install-key FILE"); wrong_args ("--install-key FILE USER-ID");
err = command_install_key (*argv); err = command_install_key (*argv, argv[1]);
break; break;
case aRemoveKey: case aRemoveKey:
@ -1339,6 +1339,81 @@ send_congratulation_message (const char *mbox, const char *keyfile)
} }
/* Write the content of SRC to the new file FNAME. */
static gpg_error_t
write_to_file (estream_t src, const char *fname)
{
gpg_error_t err;
estream_t dst;
char buffer[4096];
size_t nread, written;
dst = es_fopen (fname, "wb");
if (!dst)
return gpg_error_from_syserror ();
do
{
nread = es_fread (buffer, 1, sizeof buffer, src);
if (!nread)
break;
written = es_fwrite (buffer, 1, nread, dst);
if (written != nread)
break;
}
while (!es_feof (src) && !es_ferror (src) && !es_ferror (dst));
if (!es_feof (src) || es_ferror (src) || es_ferror (dst))
{
err = gpg_error_from_syserror ();
es_fclose (dst);
gnupg_remove (fname);
return err;
}
if (es_fclose (dst))
{
err = gpg_error_from_syserror ();
log_error ("error closing '%s': %s\n", fname, gpg_strerror (err));
return err;
}
return 0;
}
/* Compute the the full file name for the key with ADDRSPEC and return
* it at R_FNAME. */
static gpg_error_t
compute_hu_fname (char **r_fname, const char *addrspec)
{
gpg_error_t err;
char *hash;
const char *domain;
char sha1buf[20];
*r_fname = NULL;
domain = strchr (addrspec, '@');
if (!domain || !domain[1] || domain == addrspec)
return gpg_error (GPG_ERR_INV_ARG);
domain++;
gcry_md_hash_buffer (GCRY_MD_SHA1, sha1buf, addrspec, domain - addrspec - 1);
hash = zb32_encode (sha1buf, 8*20);
if (!hash)
return gpg_error_from_syserror ();
*r_fname = make_filename_try (opt.directory, domain, "hu", hash, NULL);
if (!*r_fname)
err = gpg_error_from_syserror ();
else
err = 0;
xfree (hash);
return err;
}
/* Check that we have send a request with NONCE and publish the key. */ /* Check that we have send a request with NONCE and publish the key. */
static gpg_error_t static gpg_error_t
check_and_publish (server_ctx_t ctx, const char *address, const char *nonce) check_and_publish (server_ctx_t ctx, const char *address, const char *nonce)
@ -1412,24 +1487,10 @@ check_and_publish (server_ctx_t ctx, const char *address, const char *nonce)
goto leave; goto leave;
} }
/* Hash user ID and create filename. */ /* Hash user ID and create filename. */
s = strchr (address, '@'); err = compute_hu_fname (&fnewname, address);
log_assert (s); if (err)
gcry_md_hash_buffer (GCRY_MD_SHA1, shaxbuf, address, s - address); goto leave;
hash = zb32_encode (shaxbuf, 8*20);
if (!hash)
{
err = gpg_error_from_syserror ();
goto leave;
}
fnewname = make_filename_try (opt.directory, domain, "hu", hash, NULL);
if (!fnewname)
{
err = gpg_error_from_syserror ();
goto leave;
}
/* Publish. */ /* Publish. */
err = copy_key_as_binary (fname, fnewname, address); err = copy_key_as_binary (fname, fnewname, address);
@ -1935,16 +1996,122 @@ command_cron (void)
} }
/* Install a single key into the WKD by reading FNAME. */ /* Install a single key into the WKD by reading FNAME and extracting
* USERID. */
static gpg_error_t static gpg_error_t
command_install_key (const char *fname) command_install_key (const char *fname, const char *userid)
{ {
(void)fname; gpg_error_t err;
return gpg_error (GPG_ERR_NOT_IMPLEMENTED); estream_t fp;
char *addrspec = NULL;
char *fpr = NULL;
uidinfo_list_t uidlist = NULL;
uidinfo_list_t uid, thisuid;
time_t thistime;
char *huname = NULL;
int any;
fp = es_fopen (fname, "rb");
if (!fp)
{
err = gpg_error_from_syserror ();
log_error ("error reading '%s': %s\n", fname, gpg_strerror (err));
goto leave;
}
addrspec = mailbox_from_userid (userid);
if (!addrspec)
{
log_error ("\"%s\" is not a proper mail address\n", userid);
err = gpg_error (GPG_ERR_INV_USER_ID);
goto leave;
}
/* List the key so that we can figure out the newest UID with the
* requested addrspec. */
err = wks_list_key (fp, &fpr, &uidlist);
if (err)
{
log_error ("error parsing key: %s\n", gpg_strerror (err));
err = gpg_error (GPG_ERR_NO_PUBKEY);
goto leave;
}
thistime = 0;
thisuid = NULL;
any = 0;
for (uid = uidlist; uid; uid = uid->next)
{
if (!uid->mbox)
continue; /* Should not happen anyway. */
if (ascii_strcasecmp (uid->mbox, addrspec))
continue; /* Not the requested addrspec. */
any = 1;
if (uid->created > thistime)
{
thistime = uid->created;
thisuid = uid;
}
}
if (!thisuid)
thisuid = uidlist; /* This is the case for a missing timestamp. */
if (!any)
{
log_error ("public key in '%s' has no mail address '%s'\n",
fname, addrspec);
err = gpg_error (GPG_ERR_INV_USER_ID);
goto leave;
}
if (opt.verbose)
log_info ("using key with user id '%s'\n", thisuid->uid);
{
estream_t fp2;
es_rewind (fp);
err = wks_filter_uid (&fp2, fp, thisuid->uid, 1);
if (err)
{
log_error ("error filtering key: %s\n", gpg_strerror (err));
err = gpg_error (GPG_ERR_NO_PUBKEY);
goto leave;
}
es_fclose (fp);
fp = fp2;
}
/* Hash user ID and create filename. */
err = compute_hu_fname (&huname, addrspec);
if (err)
goto leave;
/* Publish. */
err = write_to_file (fp, huname);
if (err)
{
log_error ("copying key to '%s' failed: %s\n", huname,gpg_strerror (err));
goto leave;
}
/* Make sure it is world readable. */
if (gnupg_chmod (huname, "-rwxr--r--"))
log_error ("can't set permissions of '%s': %s\n",
huname, gpg_strerror (gpg_err_code_from_syserror()));
if (!opt.quiet)
log_info ("key %s published for '%s'\n", fpr, addrspec);
leave:
xfree (huname);
free_uidinfo_list (uidlist);
xfree (fpr);
xfree (addrspec);
es_fclose (fp);
return err;
} }
/* Return the filename and optioanlly the addrspec for USERID at /* Return the filename and optionally the addrspec for USERID at
* R_FNAME and R_ADDRSPEC. R_ADDRSPEC might also be set on error. */ * R_FNAME and R_ADDRSPEC. R_ADDRSPEC might also be set on error. */
static gpg_error_t static gpg_error_t
fname_from_userid (const char *userid, char **r_fname, char **r_addrspec) fname_from_userid (const char *userid, char **r_fname, char **r_addrspec)

View File

@ -89,7 +89,7 @@ void free_uidinfo_list (uidinfo_list_t list);
gpg_error_t wks_list_key (estream_t key, char **r_fpr, gpg_error_t wks_list_key (estream_t key, char **r_fpr,
uidinfo_list_t *r_mboxes); uidinfo_list_t *r_mboxes);
gpg_error_t wks_filter_uid (estream_t *r_newkey, estream_t key, gpg_error_t wks_filter_uid (estream_t *r_newkey, estream_t key,
const char *uid); const char *uid, int binary);
gpg_error_t wks_send_mime (mime_maker_t mime); gpg_error_t wks_send_mime (mime_maker_t mime);
gpg_error_t wks_parse_policy (policy_flags_t flags, estream_t stream, gpg_error_t wks_parse_policy (policy_flags_t flags, estream_t stream,
int ignore_unknown); int ignore_unknown);

View File

@ -317,10 +317,13 @@ wks_list_key (estream_t key, char **r_fpr, uidinfo_list_t *r_mboxes)
/* Run gpg as a filter on KEY and write the output to a new stream /* 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 * stored at R_NEWKEY. The new key will contain only the user id UID.
* UID. Returns 0 on success. Only one key is expected in KEY. */ * Returns 0 on success. Only one key is expected in KEY. If BINARY
* is set the resulting key is returned as a binary (non-armored)
* keyblock. */
gpg_error_t gpg_error_t
wks_filter_uid (estream_t *r_newkey, estream_t key, const char *uid) wks_filter_uid (estream_t *r_newkey, estream_t key, const char *uid,
int binary)
{ {
gpg_error_t err; gpg_error_t err;
ccparray_t ccp; ccparray_t ccp;
@ -340,8 +343,9 @@ wks_filter_uid (estream_t *r_newkey, estream_t key, const char *uid)
} }
/* Prefix the key with the MIME content type. */ /* Prefix the key with the MIME content type. */
es_fputs ("Content-Type: application/pgp-keys\n" if (!binary)
"\n", newkey); es_fputs ("Content-Type: application/pgp-keys\n"
"\n", newkey);
filterexp = es_bsprintf ("keep-uid=uid=%s", uid); filterexp = es_bsprintf ("keep-uid=uid=%s", uid);
if (!filterexp) if (!filterexp)
@ -361,7 +365,8 @@ wks_filter_uid (estream_t *r_newkey, estream_t key, const char *uid)
ccparray_put (&ccp, "--batch"); ccparray_put (&ccp, "--batch");
ccparray_put (&ccp, "--status-fd=2"); ccparray_put (&ccp, "--status-fd=2");
ccparray_put (&ccp, "--always-trust"); ccparray_put (&ccp, "--always-trust");
ccparray_put (&ccp, "--armor"); if (!binary)
ccparray_put (&ccp, "--armor");
ccparray_put (&ccp, "--import-options=import-export"); ccparray_put (&ccp, "--import-options=import-export");
ccparray_put (&ccp, "--import-filter"); ccparray_put (&ccp, "--import-filter");
ccparray_put (&ccp, filterexp); ccparray_put (&ccp, filterexp);