gpg: If possible TRUST values now depend on signer's UID or --sender.

* g10/mainproc.c (check_sig_and_print): Add failsafe check for PK.
Pass KEYBLOCK down do check_signatures_trust.  Protect existsing error
ocde in case the signature expired.
* g10/pkclist.c (is_in_sender_list): New.
(check_signatures_trust): Add args keyblock and pk.  Add new uid based
checking code.
* g10/test-stubs.c, g10/gpgv.c: Adjust stubs.
--

GnuPG-bug-id: 4735
Signed-off-by: Werner Koch <wk@gnupg.org>
This commit is contained in:
Werner Koch 2020-06-08 20:13:25 +02:00
parent 61bb75d045
commit 5c2080f467
No known key found for this signature in database
GPG Key ID: E3FDFF218E45B72B
6 changed files with 204 additions and 49 deletions

View File

@ -2243,11 +2243,32 @@ Use @var{name} as the key to sign with. Note that this option overrides
@item --sender @var{mbox}
@opindex sender
This option has two purposes. @var{mbox} must either be a complete
user id with a proper mail address or just a mail address. When
creating a signature this option tells gpg the user id of a key used
to make a signature if the key was not directly specified by a user
id. When verifying a signature the @var{mbox} is used to restrict the
information printed by the TOFU code to matching user ids.
user ID containing a proper mail address or just a plain mail address.
The option can be given multiple times.
When creating a signature this option tells gpg the signing key's user
id used to make the signature and embeds that user ID into the created
signature (using OpenPGP's ``Signer's User ID'' subpacket). If the
option is given multiple times a suitable user ID is picked. However,
if the signing key was specified directly by using a mail address
(i.e. not by using a fingerprint or key ID) this option is used and
the mail address is embedded in the created signature.
When verifying a signature @var{mbox} is used to restrict the
information printed by the TOFU code to matching user IDs. If the
option is used and the signature contains a ``Signer's User ID''
subpacket that information is is also used to restrict the printed
information. Note that GnuPG considers only the mail address part of
a User ID.
If this option or the said subpacket is available the TRUST lines as
printed by option @option{status-fd} correspond to the corresponding
User ID; if no User ID is known the TRUST lines are computed directly
on the key and do not give any information about the User ID. In the
latter case it his highly recommended to scripts and other frontends
to evaluate the VALIDSIG line, retrieve the key and print all User IDs
along with their validity (trust) information.
@item --try-secret-key @var{name}
@opindex try-secret-key

View File

@ -299,10 +299,13 @@ g10_exit( int rc )
* We have to override the trustcheck from pkclist.c because
* this utility assumes that all keys in the keyring are trustworthy
*/
int
check_signatures_trust (ctrl_t ctrl, PKT_signature *sig)
gpg_error_t
check_signatures_trust (ctrl_t ctrl, kbnode_t kblock,
PKT_public_key *pk, PKT_signature *sig)
{
(void)ctrl;
(void)kblock;
(void)pk;
(void)sig;
return 0;
}

View File

@ -263,7 +263,8 @@ gpg_error_t keydb_search_fpr (KEYDB_HANDLE hd, const byte *fpr, size_t fprlen);
/*-- pkclist.c --*/
void show_revocation_reason (ctrl_t ctrl, PKT_public_key *pk, int mode );
int check_signatures_trust (ctrl_t ctrl, PKT_signature *sig);
gpg_error_t check_signatures_trust (ctrl_t ctrl, kbnode_t keyblock,
PKT_public_key *pk, PKT_signature *sig);
void release_pk_list (PK_LIST pk_list);
int expand_id (const char *id, strlist_t *into, unsigned int flags);

View File

@ -1848,7 +1848,7 @@ check_sig_and_print (CTX c, kbnode_t node)
{
PKT_signature *sig = node->pkt->pkt.signature;
const char *astr;
int rc;
gpg_error_t rc;
int is_expkey = 0;
int is_revkey = 0;
char *issuer_fpr = NULL;
@ -2031,8 +2031,9 @@ check_sig_and_print (CTX c, kbnode_t node)
{
rc = do_check_sig (c, node, extrahash, extrahashlen, included_pk,
NULL, &is_expkey, &is_revkey, &pk);
log_debug ("checked signature using included key block: %s\n",
gpg_strerror (rc));
if (opt.verbose)
log_debug ("checked signature using included key block: %s\n",
gpg_strerror (rc));
if (!rc)
{
/* The keyblock has been verified, we now import it. */
@ -2202,10 +2203,14 @@ check_sig_and_print (CTX c, kbnode_t node)
}
}
/* Do do something with the result of the signature checking. */
if (!rc || gpg_err_code (rc) == GPG_ERR_BAD_SIGNATURE)
{
/* We have checked the signature and the result is either a good
* signature or a bad signature. Further examination follows. */
kbnode_t un, keyblock;
int count = 0;
int keyblock_has_pk = 0; /* For failsafe check. */
int statno;
char keyid_str[50];
PKT_public_key *mainpk = NULL;
@ -2242,7 +2247,14 @@ check_sig_and_print (CTX c, kbnode_t node)
{
int valid;
if (un->pkt->pkttype==PKT_PUBLIC_KEY)
if (!keyblock_has_pk
&& (un->pkt->pkttype == PKT_PUBLIC_KEY
|| un->pkt->pkttype == PKT_PUBLIC_SUBKEY)
&& !cmp_public_keys (un->pkt->pkt.public_key, pk))
{
keyblock_has_pk = 1;
}
if (un->pkt->pkttype == PKT_PUBLIC_KEY)
{
mainpk = un->pkt->pkt.public_key;
continue;
@ -2284,9 +2296,19 @@ check_sig_and_print (CTX c, kbnode_t node)
log_printf ("\n");
count++;
/* At this point we could in theory stop because the primary
* UID flag is never set for more than one User ID per
* keyblock. However, we use this loop also for a failsafe
* check that the public key used to create the signature is
* contained in the keyring.*/
}
log_assert (mainpk);
if (!keyblock_has_pk)
{
log_error ("signature key lost from keyblock\n");
rc = gpg_error (GPG_ERR_INTERNAL);
}
/* In case we did not found a valid textual userid above
we print the first user id packet or a "[?]" instead along
@ -2442,14 +2464,15 @@ check_sig_and_print (CTX c, kbnode_t node)
{
if ((opt.verify_options & VERIFY_PKA_LOOKUPS))
pka_uri_from_sig (c, sig); /* Make sure PKA info is available. */
rc = check_signatures_trust (c->ctrl, sig);
rc = check_signatures_trust (c->ctrl, keyblock, pk, sig);
}
/* Print extra information about the signature. */
if (sig->flags.expired)
{
log_info (_("Signature expired %s\n"), asctimestamp(sig->expiredate));
rc = GPG_ERR_GENERAL; /* Need a better error here? */
if (!rc)
rc = gpg_error (GPG_ERR_GENERAL); /* Need a better error here? */
}
else if (sig->expiredate)
log_info (_("Signature expires %s\n"), asctimestamp(sig->expiredate));
@ -2526,7 +2549,7 @@ check_sig_and_print (CTX c, kbnode_t node)
if (opt.batch && rc)
g10_exit (1);
}
else
else /* Error checking the signature. (neither Good nor Bad). */
{
write_status_printf (STATUS_ERRSIG, "%08lX%08lX %d %d %02x %lu %d %s",
(ulong)sig->keyid[0], (ulong)sig->keyid[1],

View File

@ -1,6 +1,7 @@
/* pkclist.c - create a list of public keys
* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007,
* 2008, 2009, 2010 Free Software Foundation, Inc.
* Copyright (C) 1998-2020 Free Software Foundation, Inc.
* Copyright (C) 1997-2019 Werner Koch
* Copyright (C) 2015-2020 g10 Code GmbH
*
* This file is part of GnuPG.
*
@ -16,6 +17,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <https://www.gnu.org/licenses/>.
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#include <config.h>
@ -36,6 +38,7 @@
#include "../common/status.h"
#include "photoid.h"
#include "../common/i18n.h"
#include "../common/mbox-util.h"
#include "tofu.h"
#define CONTROL_D ('D' - 'A' + 1)
@ -537,44 +540,139 @@ write_trust_status (int statuscode, int trustlevel)
}
/****************
* Check whether we can trust this signature.
* Returns an error code if we should not trust this signature.
*/
int
check_signatures_trust (ctrl_t ctrl, PKT_signature *sig)
/* Return true if MBOX matches one of the names in opt.sender_list. */
static int
is_in_sender_list (const char *mbox)
{
PKT_public_key *pk = xmalloc_clear( sizeof *pk );
strlist_t sl;
for (sl = opt.sender_list; sl; sl = sl->next)
if (!strcmp (mbox, sl->d))
return 1;
return 0;
}
/* Check whether we can trust this signature. KEYBLOCK contains the
* key PK used to check the signature SIG. We need PK here in
* addition to KEYBLOCK so that we know the subkey used for
* verification. Returns an error code if we should not trust this
* signature (i.e. done by an not trusted key). */
gpg_error_t
check_signatures_trust (ctrl_t ctrl, kbnode_t keyblock, PKT_public_key *pk,
PKT_signature *sig)
{
gpg_error_t err = 0;
int uidbased = 0; /* 1 = signer's UID, 2 = use --sender option. */
unsigned int trustlevel = TRUST_UNKNOWN;
int rc=0;
PKT_public_key *mainpk;
PKT_user_id *targetuid;
const char *testedtarget = NULL;
kbnode_t n;
rc = get_pubkey_for_sig (ctrl, pk, sig, NULL);
if (rc)
{ /* this should not happen */
log_error("Ooops; the key vanished - can't check the trust\n");
rc = GPG_ERR_NO_PUBKEY;
goto leave;
}
if ( opt.trust_model==TM_ALWAYS )
if (opt.trust_model == TM_ALWAYS)
{
if( !opt.quiet )
if (!opt.quiet)
log_info(_("WARNING: Using untrusted key!\n"));
if (opt.with_fingerprint)
print_fingerprint (ctrl, NULL, pk, 1);
goto leave;
}
if(pk->flags.maybe_revoked && !pk->flags.revoked)
log_assert (keyblock->pkt->pkttype == PKT_PUBLIC_KEY);
mainpk = keyblock->pkt->pkt.public_key;
if ((pk->flags.maybe_revoked && !pk->flags.revoked)
|| (mainpk->flags.maybe_revoked && !mainpk->flags.revoked))
log_info(_("WARNING: this key might be revoked (revocation key"
" not present)\n"));
trustlevel = get_validity (ctrl, NULL, pk, NULL, sig, 1);
/* Figure out the user ID which was used to create the signature.
* Note that the Signer's UID may be not a valid addr-spec but the
* plain value from the sub-packet; thus we need to check this
* before looking for the matching User ID (our parser makes sure
* that signers_uid has only the mbox if there is an mbox). */
if (is_valid_mailbox (sig->signers_uid))
uidbased = 1; /* We got the signer's UID and it is an addr-spec. */
else if (opt.sender_list)
uidbased = 2;
else
uidbased = 0;
targetuid = NULL;
if (uidbased)
{
u32 tmpcreated = 0; /* Helper to find the lates user ID. */
PKT_user_id *tmpuid;
for (n=keyblock; n; n = n->next)
if (n->pkt->pkttype == PKT_USER_ID
&& !(tmpuid = n->pkt->pkt.user_id)->attrib_data
&& tmpuid->created /* (is valid) */
&& !tmpuid->flags.revoked
&& !tmpuid->flags.expired)
{
if (!tmpuid->mbox)
tmpuid->mbox = mailbox_from_userid (tmpuid->name, 0);
if (!tmpuid->mbox)
continue;
if (uidbased == 1)
{
if (!strcmp (tmpuid->mbox, sig->signers_uid)
&& tmpuid->created > tmpcreated)
{
tmpcreated = tmpuid->created;
targetuid = tmpuid;
}
}
else
{
if (is_in_sender_list (tmpuid->mbox)
&& tmpuid->created > tmpcreated)
{
tmpcreated = tmpuid->created;
targetuid = tmpuid;
}
}
}
/* In addition restrict based on --sender. */
if (uidbased == 1 && opt.sender_list
&& targetuid && !is_in_sender_list (targetuid->mbox))
{
testedtarget = targetuid->mbox;
targetuid = NULL;
}
if (opt.verbose && targetuid)
log_info (_("checking User ID \"%s\"\n"), targetuid->mbox);
}
trustlevel = get_validity (ctrl, NULL, pk, targetuid, sig, 1);
if (uidbased && !targetuid)
{
/* No user ID given but requested - force an undefined
* trustlevel but keep the trust flags. */
trustlevel &= ~TRUST_MASK;
trustlevel |= TRUST_UNDEFINED;
if (!opt.quiet)
{
if (testedtarget)
log_info (_("option %s given but issuer \"%s\" does not match\n"),
"--sender", testedtarget);
else if (uidbased == 1)
log_info (_("issuer \"%s\" does not match any User ID\n"),
sig->signers_uid);
else if (opt.sender_list)
log_info (_("option %s given but no matching User ID found\n"),
"--sender");
}
}
if ( (trustlevel & TRUST_FLAG_REVOKED) )
{
write_status( STATUS_KEYREVOKED );
if(pk->flags.revoked == 2)
write_status (STATUS_KEYREVOKED);
if (pk->flags.revoked == 2 || mainpk->flags.revoked == 2)
log_info(_("WARNING: This key has been revoked by its"
" designated revoker!\n"));
else
@ -593,14 +691,13 @@ check_signatures_trust (ctrl_t ctrl, PKT_signature *sig)
log_info (_("Note: This key has been disabled.\n"));
/* If we have PKA information adjust the trustlevel. */
if (sig->pka_info && sig->pka_info->valid)
if (sig->pka_info && sig->pka_info->valid && !(uidbased && !targetuid))
{
unsigned char fpr[MAX_FINGERPRINT_LEN];
PKT_public_key *primary_pk;
size_t fprlen;
int okay;
primary_pk = xmalloc_clear (sizeof *primary_pk);
get_pubkey (ctrl, primary_pk, pk->main_keyid);
fingerprint_from_pk (primary_pk, fpr, &fprlen);
@ -659,8 +756,12 @@ check_signatures_trust (ctrl_t ctrl, PKT_signature *sig)
case TRUST_UNKNOWN:
case TRUST_UNDEFINED:
write_trust_status (STATUS_TRUST_UNDEFINED, trustlevel);
log_info(_("WARNING: This key is not certified with"
" a trusted signature!\n"));
if (uidbased)
log_info(_("WARNING: The key's User ID is not certified with"
" a trusted signature!\n"));
else
log_info(_("WARNING: This key is not certified with"
" a trusted signature!\n"));
log_info(_(" There is no indication that the "
"signature belongs to the owner.\n" ));
print_fingerprint (ctrl, NULL, pk, 1);
@ -674,12 +775,16 @@ check_signatures_trust (ctrl_t ctrl, PKT_signature *sig)
log_info(_(" The signature is probably a FORGERY.\n"));
if (opt.with_fingerprint)
print_fingerprint (ctrl, NULL, pk, 1);
rc = gpg_error (GPG_ERR_BAD_SIGNATURE);
err = gpg_error (GPG_ERR_BAD_SIGNATURE);
break;
case TRUST_MARGINAL:
write_trust_status (STATUS_TRUST_MARGINAL, trustlevel);
log_info(_("WARNING: This key is not certified with"
if (uidbased)
log_info(_("WARNING: The key's User ID is not certified with"
" sufficiently trusted signatures!\n"));
else
log_info(_("WARNING: This key is not certified with"
" sufficiently trusted signatures!\n"));
log_info(_(" It is not certain that the"
" signature belongs to the owner.\n" ));
@ -700,8 +805,7 @@ check_signatures_trust (ctrl_t ctrl, PKT_signature *sig)
}
leave:
free_public_key( pk );
return rc;
return err;
}

View File

@ -57,10 +57,13 @@ g10_exit( int rc )
* We have to override the trustcheck from pkclist.c because
* this utility assumes that all keys in the keyring are trustworthy
*/
int
check_signatures_trust (ctrl_t ctrl, PKT_signature *sig)
gpg_error_t
check_signatures_trust (ctrl_t ctrl, kbnode_t kblock,
PKT_public_key *pk, PKT_signature *sig)
{
(void)ctrl;
(void)kblock;
(void)pk;
(void)sig;
return 0;
}