mirror of
git://git.gnupg.org/gnupg.git
synced 2024-11-09 21:28:51 +01:00
04ce6765f4
-- The fprint is too uncommon in our code base and to similar to fprintf.
1790 lines
53 KiB
C
1790 lines
53 KiB
C
/* pkclist.c - create a list of public keys
|
|
* 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.
|
|
*
|
|
* GnuPG is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* GnuPG is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* 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>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
|
|
#include "gpg.h"
|
|
#include "options.h"
|
|
#include "packet.h"
|
|
#include "../common/status.h"
|
|
#include "keydb.h"
|
|
#include "../common/util.h"
|
|
#include "main.h"
|
|
#include "trustdb.h"
|
|
#include "../common/ttyio.h"
|
|
#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)
|
|
|
|
static void
|
|
send_status_inv_recp (int reason, const char *name)
|
|
{
|
|
char buf[40];
|
|
|
|
snprintf (buf, sizeof buf, "%d ", reason);
|
|
write_status_text_and_buffer (STATUS_INV_RECP, buf,
|
|
name, strlen (name),
|
|
-1);
|
|
}
|
|
|
|
|
|
/****************
|
|
* Show the revocation reason as it is stored with the given signature
|
|
*/
|
|
static void
|
|
do_show_revocation_reason( PKT_signature *sig )
|
|
{
|
|
size_t n, nn;
|
|
const byte *p, *pp;
|
|
int seq = 0;
|
|
const char *text;
|
|
|
|
while ((p = enum_sig_subpkt (sig, 1, SIGSUBPKT_REVOC_REASON,
|
|
&n, &seq, NULL)) ) {
|
|
if( !n )
|
|
continue; /* invalid - just skip it */
|
|
|
|
if( *p == 0 )
|
|
text = _("No reason specified");
|
|
else if( *p == 0x01 )
|
|
text = _("Key is superseded");
|
|
else if( *p == 0x02 )
|
|
text = _("Key has been compromised");
|
|
else if( *p == 0x03 )
|
|
text = _("Key is no longer used");
|
|
else if( *p == 0x20 )
|
|
text = _("User ID is no longer valid");
|
|
else
|
|
text = NULL;
|
|
|
|
log_info ( _("reason for revocation: "));
|
|
if (text)
|
|
log_printf ("%s\n", text);
|
|
else
|
|
log_printf ("code=%02x\n", *p );
|
|
n--; p++;
|
|
pp = NULL;
|
|
do {
|
|
/* We don't want any empty lines, so skip them */
|
|
while( n && *p == '\n' ) {
|
|
p++;
|
|
n--;
|
|
}
|
|
if( n ) {
|
|
pp = memchr( p, '\n', n );
|
|
nn = pp? pp - p : n;
|
|
log_info ( _("revocation comment: ") );
|
|
es_write_sanitized (log_get_stream(), p, nn, NULL, NULL);
|
|
log_printf ("\n");
|
|
p += nn; n -= nn;
|
|
}
|
|
} while( pp );
|
|
}
|
|
}
|
|
|
|
/* Mode 0: try and find the revocation based on the pk (i.e. check
|
|
subkeys, etc.) Mode 1: use only the revocation on the main pk */
|
|
|
|
void
|
|
show_revocation_reason (ctrl_t ctrl, PKT_public_key *pk, int mode)
|
|
{
|
|
/* Hmmm, this is not so easy because we have to duplicate the code
|
|
* used in the trustdb to calculate the keyflags. We need to find
|
|
* a clean way to check revocation certificates on keys and
|
|
* signatures. And there should be no duplicate code. Because we
|
|
* enter this function only when the trustdb told us that we have
|
|
* a revoked key, we could simply look for a revocation cert and
|
|
* display this one, when there is only one. Let's try to do this
|
|
* until we have a better solution. */
|
|
KBNODE node, keyblock = NULL;
|
|
byte fingerprint[MAX_FINGERPRINT_LEN];
|
|
size_t fingerlen;
|
|
int rc;
|
|
|
|
/* get the keyblock */
|
|
fingerprint_from_pk (pk, fingerprint, &fingerlen);
|
|
rc = get_pubkey_byfpr (ctrl, NULL, &keyblock, fingerprint, fingerlen);
|
|
if( rc ) { /* that should never happen */
|
|
log_debug( "failed to get the keyblock\n");
|
|
return;
|
|
}
|
|
|
|
for( node=keyblock; node; node = node->next ) {
|
|
if( (mode && node->pkt->pkttype == PKT_PUBLIC_KEY) ||
|
|
( ( node->pkt->pkttype == PKT_PUBLIC_KEY
|
|
|| node->pkt->pkttype == PKT_PUBLIC_SUBKEY )
|
|
&& !cmp_public_keys( node->pkt->pkt.public_key, pk ) ) )
|
|
break;
|
|
}
|
|
if( !node ) {
|
|
log_debug("Oops, PK not in keyblock\n");
|
|
release_kbnode( keyblock );
|
|
return;
|
|
}
|
|
/* now find the revocation certificate */
|
|
for( node = node->next; node ; node = node->next ) {
|
|
if( node->pkt->pkttype == PKT_PUBLIC_SUBKEY )
|
|
break;
|
|
if( node->pkt->pkttype == PKT_SIGNATURE
|
|
&& (node->pkt->pkt.signature->sig_class == 0x20
|
|
|| node->pkt->pkt.signature->sig_class == 0x28 ) ) {
|
|
/* FIXME: we should check the signature here */
|
|
do_show_revocation_reason ( node->pkt->pkt.signature );
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* We didn't find it, so check if the whole key is revoked */
|
|
if(!node && !mode)
|
|
show_revocation_reason (ctrl, pk, 1);
|
|
|
|
release_kbnode( keyblock );
|
|
}
|
|
|
|
|
|
/****************
|
|
* mode: 0 = standard
|
|
* 1 = Without key info and additional menu option 'm'
|
|
* this does also add an option to set the key to ultimately trusted.
|
|
* Returns:
|
|
* -2 = nothing changed - caller should show some additional info
|
|
* -1 = quit operation
|
|
* 0 = nothing changed
|
|
* 1 = new ownertrust now in new_trust
|
|
*/
|
|
#ifndef NO_TRUST_MODELS
|
|
static int
|
|
do_edit_ownertrust (ctrl_t ctrl, PKT_public_key *pk, int mode,
|
|
unsigned *new_trust, int defer_help )
|
|
{
|
|
char *p;
|
|
u32 keyid[2];
|
|
int changed=0;
|
|
int quit=0;
|
|
int show=0;
|
|
int min_num;
|
|
int did_help=defer_help;
|
|
unsigned int minimum = tdb_get_min_ownertrust (ctrl, pk, 0);
|
|
|
|
switch(minimum)
|
|
{
|
|
default:
|
|
case TRUST_UNDEFINED: min_num=1; break;
|
|
case TRUST_NEVER: min_num=2; break;
|
|
case TRUST_MARGINAL: min_num=3; break;
|
|
case TRUST_FULLY: min_num=4; break;
|
|
}
|
|
|
|
keyid_from_pk (pk, keyid);
|
|
for(;;) {
|
|
/* A string with valid answers.
|
|
|
|
TRANSLATORS: These are the allowed answers in lower and
|
|
uppercase. Below you will find the matching strings which
|
|
should be translated accordingly and the letter changed to
|
|
match the one in the answer string.
|
|
|
|
i = please show me more information
|
|
m = back to the main menu
|
|
s = skip this key
|
|
q = quit
|
|
*/
|
|
const char *ans = _("iImMqQsS");
|
|
|
|
if( !did_help )
|
|
{
|
|
if( !mode )
|
|
{
|
|
KBNODE keyblock, un;
|
|
|
|
tty_printf (_("No trust value assigned to:\n"));
|
|
print_key_line (ctrl, NULL, pk, 0);
|
|
|
|
p = get_user_id_native (ctrl, keyid);
|
|
tty_printf (_(" \"%s\"\n"),p);
|
|
xfree (p);
|
|
|
|
keyblock = get_pubkeyblock (ctrl, keyid);
|
|
if (!keyblock)
|
|
BUG ();
|
|
for (un=keyblock; un; un = un->next)
|
|
{
|
|
if (un->pkt->pkttype != PKT_USER_ID )
|
|
continue;
|
|
if (un->pkt->pkt.user_id->flags.revoked)
|
|
continue;
|
|
if (un->pkt->pkt.user_id->flags.expired)
|
|
continue;
|
|
/* Only skip textual primaries */
|
|
if (un->pkt->pkt.user_id->flags.primary
|
|
&& !un->pkt->pkt.user_id->attrib_data )
|
|
continue;
|
|
|
|
if((opt.verify_options&VERIFY_SHOW_PHOTOS)
|
|
&& un->pkt->pkt.user_id->attrib_data)
|
|
show_photos (ctrl,
|
|
un->pkt->pkt.user_id->attribs,
|
|
un->pkt->pkt.user_id->numattribs, pk,
|
|
un->pkt->pkt.user_id);
|
|
|
|
p=utf8_to_native(un->pkt->pkt.user_id->name,
|
|
un->pkt->pkt.user_id->len,0);
|
|
|
|
tty_printf(_(" aka \"%s\"\n"),p);
|
|
}
|
|
|
|
print_fingerprint (ctrl, NULL, pk, 2);
|
|
tty_printf("\n");
|
|
release_kbnode (keyblock);
|
|
}
|
|
|
|
if(opt.trust_model==TM_DIRECT)
|
|
{
|
|
tty_printf(_("How much do you trust that this key actually "
|
|
"belongs to the named user?\n"));
|
|
tty_printf("\n");
|
|
}
|
|
else
|
|
{
|
|
/* This string also used in keyedit.c:trustsig_prompt */
|
|
tty_printf(_("Please decide how far you trust this user to"
|
|
" correctly verify other users' keys\n"
|
|
"(by looking at passports, checking fingerprints from"
|
|
" different sources, etc.)\n"));
|
|
tty_printf("\n");
|
|
}
|
|
|
|
if(min_num<=1)
|
|
tty_printf (_(" %d = I don't know or won't say\n"), 1);
|
|
if(min_num<=2)
|
|
tty_printf (_(" %d = I do NOT trust\n"), 2);
|
|
if(min_num<=3)
|
|
tty_printf (_(" %d = I trust marginally\n"), 3);
|
|
if(min_num<=4)
|
|
tty_printf (_(" %d = I trust fully\n"), 4);
|
|
if (mode)
|
|
tty_printf (_(" %d = I trust ultimately\n"), 5);
|
|
#if 0
|
|
/* not yet implemented */
|
|
tty_printf (" i = please show me more information\n");
|
|
#endif
|
|
if( mode )
|
|
tty_printf(_(" m = back to the main menu\n"));
|
|
else
|
|
{
|
|
tty_printf(_(" s = skip this key\n"));
|
|
tty_printf(_(" q = quit\n"));
|
|
}
|
|
tty_printf("\n");
|
|
if(minimum)
|
|
tty_printf(_("The minimum trust level for this key is: %s\n\n"),
|
|
trust_value_to_string(minimum));
|
|
did_help = 1;
|
|
}
|
|
if( strlen(ans) != 8 )
|
|
BUG();
|
|
p = cpr_get("edit_ownertrust.value",_("Your decision? "));
|
|
trim_spaces(p);
|
|
cpr_kill_prompt();
|
|
if( !*p )
|
|
did_help = 0;
|
|
else if( *p && p[1] )
|
|
;
|
|
else if( !p[1] && ((*p >= '0'+min_num) && *p <= (mode?'5':'4')) )
|
|
{
|
|
unsigned int trust;
|
|
switch( *p )
|
|
{
|
|
case '1': trust = TRUST_UNDEFINED; break;
|
|
case '2': trust = TRUST_NEVER ; break;
|
|
case '3': trust = TRUST_MARGINAL ; break;
|
|
case '4': trust = TRUST_FULLY ; break;
|
|
case '5': trust = TRUST_ULTIMATE ; break;
|
|
default: BUG();
|
|
}
|
|
if (trust == TRUST_ULTIMATE
|
|
&& !cpr_get_answer_is_yes ("edit_ownertrust.set_ultimate.okay",
|
|
_("Do you really want to set this key"
|
|
" to ultimate trust? (y/N) ")))
|
|
; /* no */
|
|
else
|
|
{
|
|
*new_trust = trust;
|
|
changed = 1;
|
|
break;
|
|
}
|
|
}
|
|
#if 0
|
|
/* not yet implemented */
|
|
else if( *p == ans[0] || *p == ans[1] )
|
|
{
|
|
tty_printf(_("Certificates leading to an ultimately trusted key:\n"));
|
|
show = 1;
|
|
break;
|
|
}
|
|
#endif
|
|
else if( mode && (*p == ans[2] || *p == ans[3] || *p == CONTROL_D ) )
|
|
{
|
|
break ; /* back to the menu */
|
|
}
|
|
else if( !mode && (*p == ans[6] || *p == ans[7] ) )
|
|
{
|
|
break; /* skip */
|
|
}
|
|
else if( !mode && (*p == ans[4] || *p == ans[5] ) )
|
|
{
|
|
quit = 1;
|
|
break ; /* back to the menu */
|
|
}
|
|
xfree(p); p = NULL;
|
|
}
|
|
xfree(p);
|
|
return show? -2: quit? -1 : changed;
|
|
}
|
|
#endif /*!NO_TRUST_MODELS*/
|
|
|
|
|
|
/*
|
|
* Display a menu to change the ownertrust of the key PK (which should
|
|
* be a primary key).
|
|
* For mode values see do_edit_ownertrust ()
|
|
*/
|
|
#ifndef NO_TRUST_MODELS
|
|
int
|
|
edit_ownertrust (ctrl_t ctrl, PKT_public_key *pk, int mode )
|
|
{
|
|
unsigned int trust = 0;
|
|
int no_help = 0;
|
|
|
|
for(;;)
|
|
{
|
|
switch ( do_edit_ownertrust (ctrl, pk, mode, &trust, no_help ) )
|
|
{
|
|
case -1: /* quit */
|
|
return -1;
|
|
case -2: /* show info */
|
|
no_help = 1;
|
|
break;
|
|
case 1: /* trust value set */
|
|
trust &= ~TRUST_FLAG_DISABLED;
|
|
trust |= get_ownertrust (ctrl, pk) & TRUST_FLAG_DISABLED;
|
|
update_ownertrust (ctrl, pk, trust );
|
|
return 1;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
#endif /*!NO_TRUST_MODELS*/
|
|
|
|
|
|
/****************
|
|
* Check whether we can trust this pk which has a trustlevel of TRUSTLEVEL
|
|
* Returns: true if we trust.
|
|
*/
|
|
static int
|
|
do_we_trust( PKT_public_key *pk, unsigned int trustlevel )
|
|
{
|
|
/* We should not be able to get here with a revoked or expired
|
|
key */
|
|
if(trustlevel & TRUST_FLAG_REVOKED
|
|
|| trustlevel & TRUST_FLAG_SUB_REVOKED
|
|
|| (trustlevel & TRUST_MASK) == TRUST_EXPIRED)
|
|
{
|
|
if (opt.ignore_expiration)
|
|
return 0;
|
|
BUG ();
|
|
}
|
|
|
|
if( opt.trust_model==TM_ALWAYS )
|
|
{
|
|
if( opt.verbose )
|
|
log_info("No trust check due to '--trust-model always' option\n");
|
|
return 1;
|
|
}
|
|
|
|
switch(trustlevel & TRUST_MASK)
|
|
{
|
|
default:
|
|
log_error ("invalid trustlevel %u returned from validation layer\n",
|
|
trustlevel);
|
|
/* fall through */
|
|
case TRUST_UNKNOWN:
|
|
case TRUST_UNDEFINED:
|
|
log_info(_("%s: There is no assurance this key belongs"
|
|
" to the named user\n"),keystr_from_pk(pk));
|
|
return 0; /* no */
|
|
|
|
case TRUST_MARGINAL:
|
|
log_info(_("%s: There is limited assurance this key belongs"
|
|
" to the named user\n"),keystr_from_pk(pk));
|
|
return 1; /* yes */
|
|
|
|
case TRUST_FULLY:
|
|
if( opt.verbose )
|
|
log_info(_("This key probably belongs to the named user\n"));
|
|
return 1; /* yes */
|
|
|
|
case TRUST_ULTIMATE:
|
|
if( opt.verbose )
|
|
log_info(_("This key belongs to us\n"));
|
|
return 1; /* yes */
|
|
|
|
case TRUST_NEVER:
|
|
/* This can be returned by TOFU, which can return negative
|
|
assertions. */
|
|
log_info(_("%s: This key is bad! It has been marked as untrusted!\n"),
|
|
keystr_from_pk(pk));
|
|
return 0; /* no */
|
|
}
|
|
|
|
return 1; /*NOTREACHED*/
|
|
}
|
|
|
|
|
|
/****************
|
|
* wrapper around do_we_trust, so we can ask whether to use the
|
|
* key anyway.
|
|
*/
|
|
static int
|
|
do_we_trust_pre (ctrl_t ctrl, PKT_public_key *pk, unsigned int trustlevel )
|
|
{
|
|
int rc;
|
|
|
|
rc = do_we_trust( pk, trustlevel );
|
|
|
|
if( !opt.batch && !rc )
|
|
{
|
|
print_key_info (ctrl, NULL, 0, pk, 0);
|
|
print_fingerprint (ctrl, NULL, pk, 2);
|
|
tty_printf("\n");
|
|
|
|
if ((trustlevel & TRUST_MASK) == TRUST_NEVER)
|
|
tty_printf(
|
|
_("This key is bad! It has been marked as untrusted! If you\n"
|
|
"*really* know what you are doing, you may answer the next\n"
|
|
"question with yes.\n"));
|
|
else
|
|
tty_printf(
|
|
_("It is NOT certain that the key belongs to the person named\n"
|
|
"in the user ID. If you *really* know what you are doing,\n"
|
|
"you may answer the next question with yes.\n"));
|
|
|
|
tty_printf("\n");
|
|
|
|
|
|
if (is_status_enabled ())
|
|
{
|
|
u32 kid[2];
|
|
char *hint_str;
|
|
|
|
keyid_from_pk (pk, kid);
|
|
hint_str = get_long_user_id_string (ctrl, kid);
|
|
write_status_text ( STATUS_USERID_HINT, hint_str );
|
|
xfree (hint_str);
|
|
}
|
|
|
|
if( cpr_get_answer_is_yes("untrusted_key.override",
|
|
_("Use this key anyway? (y/N) ")) )
|
|
rc = 1;
|
|
|
|
/* Hmmm: Should we set a flag to tell the user about
|
|
* his decision the next time he encrypts for this recipient?
|
|
*/
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
/* Write a TRUST_foo status line including the validation model and if
|
|
* MBOX is not NULL the targeted User ID's mbox. */
|
|
static void
|
|
write_trust_status (int statuscode, int trustlevel, const char *mbox)
|
|
{
|
|
#ifdef NO_TRUST_MODELS
|
|
write_status (statuscode);
|
|
#else /* NO_TRUST_MODELS */
|
|
int tm;
|
|
|
|
/* For the combined tofu+pgp method, we return the trust model which
|
|
* was responsible for the trustlevel. */
|
|
if (opt.trust_model == TM_TOFU_PGP)
|
|
tm = (trustlevel & TRUST_FLAG_TOFU_BASED)? TM_TOFU : TM_PGP;
|
|
else
|
|
tm = opt.trust_model;
|
|
|
|
if (mbox)
|
|
{
|
|
char *escmbox = percent_escape (mbox, NULL);
|
|
|
|
write_status_strings (statuscode, "0 ", trust_model_string (tm),
|
|
" ", escmbox? escmbox : "?", NULL);
|
|
xfree (escmbox);
|
|
}
|
|
else
|
|
write_status_strings (statuscode, "0 ", trust_model_string (tm), NULL);
|
|
|
|
#endif /* NO_TRUST_MODELS */
|
|
}
|
|
|
|
|
|
/* Return true if MBOX matches one of the names in opt.sender_list. */
|
|
static int
|
|
is_in_sender_list (const char *mbox)
|
|
{
|
|
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;
|
|
PKT_public_key *mainpk;
|
|
PKT_user_id *targetuid;
|
|
const char *testedtarget = NULL;
|
|
const char *statusmbox = NULL;
|
|
kbnode_t n;
|
|
|
|
if (opt.trust_model == TM_ALWAYS)
|
|
{
|
|
if (!opt.quiet)
|
|
log_info(_("WARNING: Using untrusted key!\n"));
|
|
if (opt.with_fingerprint)
|
|
print_fingerprint (ctrl, NULL, pk, 1);
|
|
goto leave;
|
|
}
|
|
|
|
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"));
|
|
|
|
/* 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 latest 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 (uidbased && !targetuid)
|
|
statusmbox = testedtarget? testedtarget : sig->signers_uid;
|
|
else if (uidbased)
|
|
statusmbox = targetuid->mbox;
|
|
else
|
|
statusmbox = NULL;
|
|
|
|
if (opt.verbose && statusmbox)
|
|
log_info (_("checking User ID \"%s\"\n"), statusmbox);
|
|
|
|
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 || mainpk->flags.revoked == 2)
|
|
log_info(_("WARNING: This key has been revoked by its"
|
|
" designated revoker!\n"));
|
|
else
|
|
log_info(_("WARNING: This key has been revoked by its owner!\n"));
|
|
log_info(_(" This could mean that the signature is forged.\n"));
|
|
show_revocation_reason (ctrl, pk, 0);
|
|
}
|
|
else if ((trustlevel & TRUST_FLAG_SUB_REVOKED) )
|
|
{
|
|
write_status( STATUS_KEYREVOKED );
|
|
log_info(_("WARNING: This subkey has been revoked by its owner!\n"));
|
|
show_revocation_reason (ctrl, pk, 0);
|
|
}
|
|
|
|
if ((trustlevel & TRUST_FLAG_DISABLED))
|
|
log_info (_("Note: This key has been disabled.\n"));
|
|
|
|
/* Now let the user know what up with the trustlevel. */
|
|
switch ( (trustlevel & TRUST_MASK) )
|
|
{
|
|
case TRUST_EXPIRED:
|
|
log_info(_("Note: This key has expired!\n"));
|
|
print_fingerprint (ctrl, NULL, pk, 1);
|
|
break;
|
|
|
|
default:
|
|
log_error ("invalid trustlevel %u returned from validation layer\n",
|
|
trustlevel);
|
|
/* fall through */
|
|
case TRUST_UNKNOWN:
|
|
case TRUST_UNDEFINED:
|
|
write_trust_status (STATUS_TRUST_UNDEFINED, trustlevel, statusmbox);
|
|
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);
|
|
break;
|
|
|
|
case TRUST_NEVER:
|
|
/* This level can be returned by TOFU, which supports negative
|
|
* assertions. */
|
|
write_trust_status (STATUS_TRUST_NEVER, trustlevel, statusmbox);
|
|
log_info(_("WARNING: We do NOT trust this key!\n"));
|
|
log_info(_(" The signature is probably a FORGERY.\n"));
|
|
if (opt.with_fingerprint)
|
|
print_fingerprint (ctrl, NULL, pk, 1);
|
|
err = gpg_error (GPG_ERR_BAD_SIGNATURE);
|
|
break;
|
|
|
|
case TRUST_MARGINAL:
|
|
write_trust_status (STATUS_TRUST_MARGINAL, trustlevel, statusmbox);
|
|
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" ));
|
|
print_fingerprint (ctrl, NULL, pk, 1);
|
|
break;
|
|
|
|
case TRUST_FULLY:
|
|
write_trust_status (STATUS_TRUST_FULLY, trustlevel, statusmbox);
|
|
if (opt.with_fingerprint)
|
|
print_fingerprint (ctrl, NULL, pk, 1);
|
|
break;
|
|
|
|
case TRUST_ULTIMATE:
|
|
write_trust_status (STATUS_TRUST_ULTIMATE, trustlevel, statusmbox);
|
|
if (opt.with_fingerprint)
|
|
print_fingerprint (ctrl, NULL, pk, 1);
|
|
break;
|
|
}
|
|
|
|
leave:
|
|
return err;
|
|
}
|
|
|
|
|
|
void
|
|
release_pk_list (pk_list_t pk_list)
|
|
{
|
|
PK_LIST pk_rover;
|
|
|
|
for ( ; pk_list; pk_list = pk_rover)
|
|
{
|
|
pk_rover = pk_list->next;
|
|
free_public_key ( pk_list->pk );
|
|
xfree ( pk_list );
|
|
}
|
|
}
|
|
|
|
|
|
static int
|
|
key_present_in_pk_list(PK_LIST pk_list, PKT_public_key *pk)
|
|
{
|
|
for( ; pk_list; pk_list = pk_list->next)
|
|
if (cmp_public_keys(pk_list->pk, pk) == 0)
|
|
return 0;
|
|
|
|
return -1;
|
|
}
|
|
|
|
|
|
/*
|
|
* Return a malloced string with a default recipient if there is any
|
|
* Fixme: We don't distinguish between malloc failure and no-default-recipient.
|
|
*/
|
|
static char *
|
|
default_recipient (ctrl_t ctrl)
|
|
{
|
|
PKT_public_key *pk;
|
|
char *result;
|
|
|
|
if (opt.def_recipient)
|
|
return xtrystrdup (opt.def_recipient);
|
|
|
|
if (!opt.def_recipient_self)
|
|
return NULL;
|
|
pk = xtrycalloc (1, sizeof *pk );
|
|
if (!pk)
|
|
return NULL;
|
|
if (get_seckey_default (ctrl, pk))
|
|
{
|
|
free_public_key (pk);
|
|
return NULL;
|
|
}
|
|
result = hexfingerprint (pk, NULL, 0);
|
|
free_public_key (pk);
|
|
return result;
|
|
}
|
|
|
|
|
|
/* Helper for build_pk_list to find and check one key. This helper is
|
|
* also used directly in server mode by the RECIPIENTS command. On
|
|
* success the new key is added to PK_LIST_ADDR. NAME is the user id
|
|
* of the key. USE the requested usage and a set MARK_HIDDEN will
|
|
* mark the key in the updated list as a hidden recipient. If
|
|
* FROM_FILE is true, NAME is not a user ID but the name of a file
|
|
* holding a key. */
|
|
gpg_error_t
|
|
find_and_check_key (ctrl_t ctrl, const char *name, unsigned int use,
|
|
int mark_hidden, int from_file, pk_list_t *pk_list_addr)
|
|
{
|
|
int rc;
|
|
PKT_public_key *pk;
|
|
kbnode_t keyblock = NULL;
|
|
kbnode_t node;
|
|
|
|
if (!name || !*name)
|
|
return gpg_error (GPG_ERR_INV_USER_ID);
|
|
|
|
pk = xtrycalloc (1, sizeof *pk);
|
|
if (!pk)
|
|
return gpg_error_from_syserror ();
|
|
pk->req_usage = use;
|
|
|
|
if (from_file)
|
|
rc = get_pubkey_fromfile (ctrl, pk, name, &keyblock);
|
|
else
|
|
rc = get_best_pubkey_byname (ctrl, GET_PUBKEY_NORMAL,
|
|
NULL, pk, name, &keyblock, 0);
|
|
if (rc)
|
|
{
|
|
int code;
|
|
|
|
/* Key not found or other error. */
|
|
log_error (_("%s: skipped: %s\n"), name, gpg_strerror (rc) );
|
|
switch (gpg_err_code (rc))
|
|
{
|
|
case GPG_ERR_NO_SECKEY:
|
|
case GPG_ERR_NO_PUBKEY: code = 1; break;
|
|
case GPG_ERR_INV_USER_ID: code = 14; break;
|
|
default: code = 0; break;
|
|
}
|
|
send_status_inv_recp (code, name);
|
|
free_public_key (pk);
|
|
return rc;
|
|
}
|
|
|
|
rc = openpgp_pk_test_algo2 (pk->pubkey_algo, use);
|
|
if (rc)
|
|
{
|
|
/* Key found but not usable for us (e.g. sign-only key). */
|
|
release_kbnode (keyblock);
|
|
send_status_inv_recp (3, name); /* Wrong key usage */
|
|
log_error (_("%s: skipped: %s\n"), name, gpg_strerror (rc) );
|
|
free_public_key (pk);
|
|
return rc;
|
|
}
|
|
|
|
/* Key found and usable. Check validity. */
|
|
if (!from_file)
|
|
{
|
|
int trustlevel;
|
|
|
|
trustlevel = get_validity (ctrl, keyblock, pk, pk->user_id, NULL, 1);
|
|
if ( (trustlevel & TRUST_FLAG_DISABLED) )
|
|
{
|
|
/* Key has been disabled. */
|
|
release_kbnode (keyblock);
|
|
send_status_inv_recp (13, name);
|
|
log_info (_("%s: skipped: public key is disabled\n"), name);
|
|
free_public_key (pk);
|
|
return GPG_ERR_UNUSABLE_PUBKEY;
|
|
}
|
|
|
|
if ( !do_we_trust_pre (ctrl, pk, trustlevel) )
|
|
{
|
|
/* We don't trust this key. */
|
|
release_kbnode (keyblock);
|
|
send_status_inv_recp (10, name);
|
|
free_public_key (pk);
|
|
return GPG_ERR_UNUSABLE_PUBKEY;
|
|
}
|
|
}
|
|
|
|
/* Skip the actual key if the key is already present in the
|
|
list. */
|
|
if (!key_present_in_pk_list (*pk_list_addr, pk))
|
|
{
|
|
if (!opt.quiet)
|
|
log_info (_("%s: skipped: public key already present\n"), name);
|
|
free_public_key (pk);
|
|
}
|
|
else
|
|
{
|
|
pk_list_t r;
|
|
|
|
r = xmalloc (sizeof *r);
|
|
r->pk = pk;
|
|
r->next = *pk_list_addr;
|
|
r->flags = mark_hidden? 1:0;
|
|
*pk_list_addr = r;
|
|
}
|
|
|
|
for (node = keyblock; node; node = node->next)
|
|
if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY
|
|
&& ((pk=node->pkt->pkt.public_key)->pubkey_usage & PUBKEY_USAGE_RENC)
|
|
&& pk->flags.valid
|
|
&& !pk->flags.revoked
|
|
&& !pk->flags.disabled
|
|
&& !pk->has_expired
|
|
&& key_present_in_pk_list (*pk_list_addr, pk))
|
|
{
|
|
pk_list_t r;
|
|
|
|
r = xmalloc (sizeof *r);
|
|
r->pk = copy_public_key (NULL, pk);
|
|
r->next = *pk_list_addr;
|
|
r->flags = mark_hidden? 1:0; /* FIXME: Use PK_LIST_HIDDEN ? */
|
|
*pk_list_addr = r;
|
|
}
|
|
|
|
|
|
release_kbnode (keyblock);
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
/* This is the central function to collect the keys for recipients.
|
|
* It is thus used to prepare a public key encryption. encrypt-to
|
|
* keys, default keys and the keys for the actual recipients are all
|
|
* collected here. When not in batch mode and no recipient has been
|
|
* passed on the commandline, the function will also ask for
|
|
* recipients.
|
|
*
|
|
* RCPTS is a string list with the recipients; NULL is an allowed
|
|
* value but not very useful. Group expansion is done on these names;
|
|
* they may be in any of the user Id formats we can handle. The flags
|
|
* bits for each string in the string list are used for:
|
|
*
|
|
* - PK_LIST_ENCRYPT_TO :: This is an encrypt-to recipient.
|
|
* - PK_LIST_HIDDEN :: This is a hidden recipient.
|
|
* - PK_LIST_FROM_FILE :: The argument is a file with a key.
|
|
*
|
|
* On success a list of keys is stored at the address RET_PK_LIST; the
|
|
* caller must free this list. On error the value at this address is
|
|
* not changed.
|
|
*/
|
|
int
|
|
build_pk_list (ctrl_t ctrl, strlist_t rcpts, PK_LIST *ret_pk_list)
|
|
{
|
|
PK_LIST pk_list = NULL;
|
|
PKT_public_key *pk=NULL;
|
|
int rc=0;
|
|
int any_recipients=0;
|
|
strlist_t rov,remusr;
|
|
char *def_rec = NULL;
|
|
char pkstrbuf[PUBKEY_STRING_SIZE];
|
|
|
|
/* Try to expand groups if any have been defined. */
|
|
if (opt.grouplist)
|
|
remusr = expand_group (rcpts, 0);
|
|
else
|
|
remusr = rcpts;
|
|
|
|
/* XXX: Change this function to use get_pubkeys instead of
|
|
get_pubkey_byname to detect ambiguous key specifications and warn
|
|
about duplicate keyblocks. For ambiguous key specifications on
|
|
the command line or provided interactively, prompt the user to
|
|
select the best key. If a key specification is ambiguous and we
|
|
are in batch mode, die. */
|
|
|
|
if (opt.encrypt_to_default_key)
|
|
{
|
|
static int warned;
|
|
|
|
const char *default_key = parse_def_secret_key (ctrl);
|
|
if (default_key)
|
|
{
|
|
PK_LIST r = xmalloc_clear (sizeof *r);
|
|
|
|
r->pk = xmalloc_clear (sizeof *r->pk);
|
|
r->pk->req_usage = PUBKEY_USAGE_ENC;
|
|
|
|
rc = get_pubkey_byname (ctrl, GET_PUBKEY_NO_AKL,
|
|
NULL, r->pk, default_key, NULL, NULL, 0);
|
|
if (rc)
|
|
{
|
|
xfree (r->pk);
|
|
xfree (r);
|
|
|
|
log_error (_("can't encrypt to '%s'\n"), default_key);
|
|
if (!opt.quiet)
|
|
log_info (_("(check argument of option '%s')\n"),
|
|
"--default-key");
|
|
}
|
|
else
|
|
{
|
|
r->next = pk_list;
|
|
r->flags = 0;
|
|
pk_list = r;
|
|
}
|
|
}
|
|
else if (opt.def_secret_key)
|
|
{
|
|
if (! warned)
|
|
log_info (_("option '%s' given, but no valid default keys given\n"),
|
|
"--encrypt-to-default-key");
|
|
warned = 1;
|
|
}
|
|
else
|
|
{
|
|
if (! warned)
|
|
log_info (_("option '%s' given, but option '%s' not given\n"),
|
|
"--encrypt-to-default-key", "--default-key");
|
|
warned = 1;
|
|
}
|
|
}
|
|
|
|
/* Check whether there are any recipients in the list and build the
|
|
* list of the encrypt-to ones (we always trust them). */
|
|
for ( rov = remusr; rov; rov = rov->next )
|
|
{
|
|
if ( !(rov->flags & PK_LIST_ENCRYPT_TO) )
|
|
{
|
|
/* This is a regular recipient; i.e. not an encrypt-to
|
|
one. */
|
|
any_recipients = 1;
|
|
|
|
/* Hidden recipients are not allowed while in PGP mode,
|
|
issue a warning and switch into GnuPG mode. */
|
|
if ((rov->flags & PK_LIST_HIDDEN) && (PGP7 || PGP8))
|
|
{
|
|
log_info(_("option '%s' may not be used in %s mode\n"),
|
|
"--hidden-recipient",
|
|
gnupg_compliance_option_string (opt.compliance));
|
|
|
|
compliance_failure();
|
|
}
|
|
}
|
|
else if (!opt.no_encrypt_to)
|
|
{
|
|
/* --encrypt-to has not been disabled. Check this
|
|
encrypt-to key. */
|
|
pk = xmalloc_clear( sizeof *pk );
|
|
pk->req_usage = PUBKEY_USAGE_ENC;
|
|
|
|
/* We explicitly allow encrypt-to to an disabled key; thus
|
|
we pass 1 for the second last argument and 1 as the last
|
|
argument to disable AKL. */
|
|
if ((rc = get_pubkey_byname (ctrl, GET_PUBKEY_NO_AKL,
|
|
NULL, pk, rov->d, NULL, NULL, 1)))
|
|
{
|
|
free_public_key ( pk ); pk = NULL;
|
|
log_error (_("%s: skipped: %s\n"), rov->d, gpg_strerror (rc) );
|
|
send_status_inv_recp (0, rov->d);
|
|
goto fail;
|
|
}
|
|
else if ( !(rc=openpgp_pk_test_algo2 (pk->pubkey_algo,
|
|
PUBKEY_USAGE_ENC)) )
|
|
{
|
|
/* Skip the actual key if the key is already present
|
|
* in the list. Add it to our list if not. */
|
|
if (key_present_in_pk_list(pk_list, pk) == 0)
|
|
{
|
|
free_public_key (pk); pk = NULL;
|
|
if (!opt.quiet)
|
|
log_info (_("%s: skipped: public key already present\n"),
|
|
rov->d);
|
|
}
|
|
else
|
|
{
|
|
PK_LIST r;
|
|
r = xmalloc( sizeof *r );
|
|
r->pk = pk; pk = NULL;
|
|
r->next = pk_list;
|
|
r->flags = (rov->flags&PK_LIST_HIDDEN)?1:0;
|
|
pk_list = r;
|
|
|
|
/* Hidden encrypt-to recipients are not allowed while
|
|
in PGP mode, issue a warning and switch into
|
|
GnuPG mode. */
|
|
if ((r->flags&PK_LIST_ENCRYPT_TO) && (PGP7 || PGP8))
|
|
{
|
|
log_info(_("option '%s' may not be used in %s mode\n"),
|
|
"--hidden-encrypt-to",
|
|
gnupg_compliance_option_string (opt.compliance));
|
|
|
|
compliance_failure();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* The public key is not usable for encryption. */
|
|
free_public_key( pk ); pk = NULL;
|
|
log_error(_("%s: skipped: %s\n"), rov->d, gpg_strerror (rc) );
|
|
send_status_inv_recp (3, rov->d); /* Wrong key usage */
|
|
goto fail;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* If we don't have any recipients yet and we are not in batch mode
|
|
drop into interactive selection mode. */
|
|
if ( !any_recipients && !opt.batch )
|
|
{
|
|
int have_def_rec;
|
|
char *answer = NULL;
|
|
strlist_t backlog = NULL;
|
|
|
|
if (pk_list)
|
|
any_recipients = 1;
|
|
def_rec = default_recipient(ctrl);
|
|
have_def_rec = !!def_rec;
|
|
if ( !have_def_rec )
|
|
tty_printf(_("You did not specify a user ID. (you may use \"-r\")\n"));
|
|
|
|
for (;;)
|
|
{
|
|
rc = 0;
|
|
xfree(answer);
|
|
if ( have_def_rec )
|
|
{
|
|
/* A default recipient is taken as the first entry. */
|
|
answer = def_rec;
|
|
def_rec = NULL;
|
|
}
|
|
else if (backlog)
|
|
{
|
|
/* This is part of our trick to expand and display groups. */
|
|
answer = strlist_pop (&backlog);
|
|
}
|
|
else
|
|
{
|
|
/* Show the list of already collected recipients and ask
|
|
for more. */
|
|
PK_LIST iter;
|
|
|
|
tty_printf("\n");
|
|
tty_printf(_("Current recipients:\n"));
|
|
for (iter=pk_list;iter;iter=iter->next)
|
|
{
|
|
u32 keyid[2];
|
|
|
|
keyid_from_pk(iter->pk,keyid);
|
|
tty_printf ("%s/%s %s \"",
|
|
pubkey_string (iter->pk,
|
|
pkstrbuf, sizeof pkstrbuf),
|
|
keystr(keyid),
|
|
datestr_from_pk (iter->pk));
|
|
|
|
if (iter->pk->user_id)
|
|
tty_print_utf8_string(iter->pk->user_id->name,
|
|
iter->pk->user_id->len);
|
|
else
|
|
{
|
|
size_t n;
|
|
char *p = get_user_id (ctrl, keyid, &n, NULL);
|
|
tty_print_utf8_string ( p, n );
|
|
xfree(p);
|
|
}
|
|
tty_printf("\"\n");
|
|
}
|
|
|
|
answer = cpr_get_utf8("pklist.user_id.enter",
|
|
_("\nEnter the user ID. "
|
|
"End with an empty line: "));
|
|
trim_spaces(answer);
|
|
cpr_kill_prompt();
|
|
}
|
|
|
|
if ( !answer || !*answer )
|
|
{
|
|
xfree(answer);
|
|
break; /* No more recipients entered - get out of loop. */
|
|
}
|
|
|
|
/* Do group expand here too. The trick here is to continue
|
|
the loop if any expansion occurred. The code above will
|
|
then list all expanded keys. */
|
|
if (expand_id(answer,&backlog,0))
|
|
continue;
|
|
|
|
/* Get and check key for the current name. */
|
|
free_public_key (pk);
|
|
pk = xmalloc_clear( sizeof *pk );
|
|
pk->req_usage = PUBKEY_USAGE_ENC;
|
|
rc = get_pubkey_byname (ctrl, GET_PUBKEY_NORMAL,
|
|
NULL, pk, answer, NULL, NULL, 0);
|
|
if (rc)
|
|
tty_printf(_("No such user ID.\n"));
|
|
else if ( !(rc=openpgp_pk_test_algo2 (pk->pubkey_algo,
|
|
PUBKEY_USAGE_ENC)) )
|
|
{
|
|
if ( have_def_rec )
|
|
{
|
|
/* No validation for a default recipient. */
|
|
if (!key_present_in_pk_list(pk_list, pk))
|
|
{
|
|
free_public_key (pk);
|
|
pk = NULL;
|
|
log_info (_("skipped: public key "
|
|
"already set as default recipient\n") );
|
|
}
|
|
else
|
|
{
|
|
PK_LIST r = xmalloc (sizeof *r);
|
|
r->pk = pk; pk = NULL;
|
|
r->next = pk_list;
|
|
r->flags = 0; /* No throwing default ids. */
|
|
pk_list = r;
|
|
}
|
|
any_recipients = 1;
|
|
continue;
|
|
}
|
|
else
|
|
{ /* Check validity of this key. */
|
|
int trustlevel;
|
|
|
|
trustlevel =
|
|
get_validity (ctrl, NULL, pk, pk->user_id, NULL, 1);
|
|
if ( (trustlevel & TRUST_FLAG_DISABLED) )
|
|
{
|
|
tty_printf (_("Public key is disabled.\n") );
|
|
}
|
|
else if ( do_we_trust_pre (ctrl, pk, trustlevel) )
|
|
{
|
|
/* Skip the actual key if the key is already
|
|
* present in the list */
|
|
if (!key_present_in_pk_list(pk_list, pk))
|
|
{
|
|
free_public_key (pk);
|
|
pk = NULL;
|
|
log_info(_("skipped: public key already set\n") );
|
|
}
|
|
else
|
|
{
|
|
PK_LIST r;
|
|
r = xmalloc( sizeof *r );
|
|
r->pk = pk; pk = NULL;
|
|
r->next = pk_list;
|
|
r->flags = 0; /* No throwing interactive ids. */
|
|
pk_list = r;
|
|
}
|
|
any_recipients = 1;
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
xfree(def_rec); def_rec = NULL;
|
|
have_def_rec = 0;
|
|
}
|
|
if ( pk )
|
|
{
|
|
free_public_key( pk );
|
|
pk = NULL;
|
|
}
|
|
}
|
|
else if ( !any_recipients && (def_rec = default_recipient(ctrl)) )
|
|
{
|
|
/* We are in batch mode and have only a default recipient. */
|
|
pk = xmalloc_clear( sizeof *pk );
|
|
pk->req_usage = PUBKEY_USAGE_ENC;
|
|
|
|
/* The default recipient is allowed to be disabled; thus pass 1
|
|
as second last argument. We also don't want an AKL. */
|
|
rc = get_pubkey_byname (ctrl, GET_PUBKEY_NO_AKL,
|
|
NULL, pk, def_rec, NULL, NULL, 1);
|
|
if (rc)
|
|
log_error(_("unknown default recipient \"%s\"\n"), def_rec );
|
|
else if ( !(rc=openpgp_pk_test_algo2(pk->pubkey_algo,
|
|
PUBKEY_USAGE_ENC)) )
|
|
{
|
|
/* Mark any_recipients here since the default recipient
|
|
would have been used if it wasn't already there. It
|
|
doesn't really matter if we got this key from the default
|
|
recipient or an encrypt-to. */
|
|
any_recipients = 1;
|
|
if (!key_present_in_pk_list(pk_list, pk))
|
|
log_info (_("skipped: public key already set "
|
|
"as default recipient\n"));
|
|
else
|
|
{
|
|
PK_LIST r = xmalloc( sizeof *r );
|
|
r->pk = pk; pk = NULL;
|
|
r->next = pk_list;
|
|
r->flags = 0; /* No throwing default ids. */
|
|
pk_list = r;
|
|
}
|
|
}
|
|
if ( pk )
|
|
{
|
|
free_public_key( pk );
|
|
pk = NULL;
|
|
}
|
|
xfree(def_rec); def_rec = NULL;
|
|
}
|
|
else
|
|
{
|
|
/* General case: Check all keys. */
|
|
any_recipients = 0;
|
|
for (; remusr; remusr = remusr->next )
|
|
{
|
|
if ( (remusr->flags & PK_LIST_ENCRYPT_TO) )
|
|
continue; /* encrypt-to keys are already handled. */
|
|
|
|
rc = find_and_check_key (ctrl, remusr->d, PUBKEY_USAGE_ENC,
|
|
!!(remusr->flags&PK_LIST_HIDDEN),
|
|
!!(remusr->flags&PK_LIST_FROM_FILE),
|
|
&pk_list);
|
|
if (rc)
|
|
goto fail;
|
|
any_recipients = 1;
|
|
}
|
|
}
|
|
|
|
if ( !rc && !any_recipients )
|
|
{
|
|
log_error(_("no valid addressees\n"));
|
|
write_status_text (STATUS_NO_RECP, "0");
|
|
rc = GPG_ERR_NO_USER_ID;
|
|
}
|
|
|
|
#ifdef USE_TOFU
|
|
if (! rc && (opt.trust_model == TM_TOFU_PGP || opt.trust_model == TM_TOFU))
|
|
{
|
|
PK_LIST iter;
|
|
for (iter = pk_list; iter; iter = iter->next)
|
|
{
|
|
int rc2;
|
|
|
|
/* Note: we already resolved any conflict when looking up
|
|
the key. Don't annoy the user again if she selected
|
|
accept once. */
|
|
rc2 = tofu_register_encryption (ctrl, iter->pk, NULL, 0);
|
|
if (rc2)
|
|
log_info ("WARNING: Failed to register encryption to %s"
|
|
" with TOFU engine\n",
|
|
keystr (pk_main_keyid (iter->pk)));
|
|
else if (DBG_TRUST)
|
|
log_debug ("Registered encryption to %s with TOFU DB.\n",
|
|
keystr (pk_main_keyid (iter->pk)));
|
|
}
|
|
}
|
|
#endif /*USE_TOFU*/
|
|
|
|
fail:
|
|
|
|
if ( rc )
|
|
release_pk_list( pk_list );
|
|
else
|
|
*ret_pk_list = pk_list;
|
|
if (opt.grouplist)
|
|
free_strlist(remusr);
|
|
return rc;
|
|
}
|
|
|
|
|
|
/* In pgp6 mode, disallow all ciphers except IDEA (1), 3DES (2), and
|
|
CAST5 (3), all hashes except MD5 (1), SHA1 (2), and RIPEMD160 (3),
|
|
and all compressions except none (0) and ZIP (1). pgp7 and pgp8
|
|
mode expands the cipher list to include AES128 (7), AES192 (8),
|
|
AES256 (9), and TWOFISH (10). pgp8 adds the SHA-256 hash (8). For
|
|
a true PGP key all of this is unneeded as they are the only items
|
|
present in the preferences subpacket, but checking here covers the
|
|
weird case of encrypting to a key that had preferences from a
|
|
different implementation which was then used with PGP. I am not
|
|
completely comfortable with this as the right thing to do, as it
|
|
slightly alters the list of what the user is supposedly requesting.
|
|
It is not against the RFC however, as the preference chosen will
|
|
never be one that the user didn't specify somewhere ("The
|
|
implementation may use any mechanism to pick an algorithm in the
|
|
intersection"), and PGP has no mechanism to fix such a broken
|
|
preference list, so I'm including it. -dms */
|
|
|
|
int
|
|
algo_available( preftype_t preftype, int algo, const struct pref_hint *hint)
|
|
{
|
|
if( preftype == PREFTYPE_SYM )
|
|
{
|
|
if (!opt.flags.allow_old_cipher_algos
|
|
&& openpgp_cipher_blocklen (algo) < 16)
|
|
return 0; /* We don't want this one. */
|
|
|
|
if(PGP7 && (algo != CIPHER_ALGO_IDEA
|
|
&& algo != CIPHER_ALGO_3DES
|
|
&& algo != CIPHER_ALGO_CAST5
|
|
&& algo != CIPHER_ALGO_AES
|
|
&& algo != CIPHER_ALGO_AES192
|
|
&& algo != CIPHER_ALGO_AES256
|
|
&& algo != CIPHER_ALGO_TWOFISH))
|
|
return 0;
|
|
|
|
/* PGP8 supports all the ciphers we do.. */
|
|
|
|
return algo && !openpgp_cipher_test_algo ( algo );
|
|
}
|
|
else if( preftype == PREFTYPE_HASH )
|
|
{
|
|
if (hint && hint->digest_length)
|
|
{
|
|
unsigned int n = gcry_md_get_algo_dlen (algo);
|
|
|
|
if (hint->exact)
|
|
{
|
|
/* For example ECDSA requires an exact hash value so
|
|
* that we do not truncate. For DSA we allow truncation
|
|
* and thus exact is not set. */
|
|
if (hint->digest_length != n)
|
|
return 0;
|
|
}
|
|
else if (hint->digest_length!=20 || opt.flags.dsa2)
|
|
{
|
|
/* If --enable-dsa2 is set or the hash isn't 160 bits
|
|
(which implies DSA2), then we'll accept a hash that
|
|
is larger than we need. Otherwise we won't accept
|
|
any hash that isn't exactly the right size. */
|
|
if (hint->digest_length > n)
|
|
return 0;
|
|
}
|
|
else if (hint->digest_length != n)
|
|
return 0;
|
|
}
|
|
|
|
if (PGP7 && (algo != DIGEST_ALGO_MD5
|
|
&& algo != DIGEST_ALGO_SHA1
|
|
&& algo != DIGEST_ALGO_RMD160))
|
|
return 0;
|
|
|
|
|
|
if(PGP8 && (algo != DIGEST_ALGO_MD5
|
|
&& algo != DIGEST_ALGO_SHA1
|
|
&& algo != DIGEST_ALGO_RMD160
|
|
&& algo != DIGEST_ALGO_SHA256))
|
|
return 0;
|
|
|
|
return algo && !openpgp_md_test_algo (algo);
|
|
}
|
|
else if( preftype == PREFTYPE_ZIP )
|
|
{
|
|
if (PGP7 && (algo != COMPRESS_ALGO_NONE
|
|
&& algo != COMPRESS_ALGO_ZIP))
|
|
return 0;
|
|
|
|
/* PGP8 supports all the compression algos we do */
|
|
|
|
return !check_compress_algo( algo );
|
|
}
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
/****************
|
|
* Return -1 if we could not find an algorithm.
|
|
*/
|
|
int
|
|
select_algo_from_prefs(PK_LIST pk_list, int preftype,
|
|
int request, const struct pref_hint *hint)
|
|
{
|
|
PK_LIST pkr;
|
|
u32 bits[8];
|
|
const prefitem_t *prefs;
|
|
int result=-1,i;
|
|
u16 scores[256];
|
|
|
|
if( !pk_list )
|
|
return -1;
|
|
|
|
memset(bits,0xFF,sizeof(bits));
|
|
memset(scores,0,sizeof(scores));
|
|
|
|
for( pkr = pk_list; pkr; pkr = pkr->next )
|
|
{
|
|
u32 mask[8];
|
|
int rank=1,implicit=-1;
|
|
|
|
memset(mask,0,sizeof(mask));
|
|
|
|
switch(preftype)
|
|
{
|
|
case PREFTYPE_SYM:
|
|
/* Historical note: IDEA is implicitly there for v3 keys
|
|
with v3 selfsigs if --pgp2 mode is on. This was a 2440
|
|
thing that was dropped from 4880 but is still relevant to
|
|
GPG's 1991 support. All this doesn't mean IDEA is
|
|
actually available, of course. */
|
|
if (opt.flags.allow_old_cipher_algos)
|
|
implicit = CIPHER_ALGO_3DES;
|
|
else
|
|
implicit = CIPHER_ALGO_AES;
|
|
break;
|
|
|
|
case PREFTYPE_AEAD:
|
|
/* No implicit algo. */
|
|
break;
|
|
|
|
case PREFTYPE_HASH:
|
|
/* While I am including this code for completeness, note
|
|
that currently --pgp2 mode locks the hash at MD5, so this
|
|
code will never even be called. Even if the hash wasn't
|
|
locked at MD5, we don't support sign+encrypt in --pgp2
|
|
mode, and that's the only time PREFTYPE_HASH is used
|
|
anyway. -dms
|
|
|
|
Because "de-vs" compliance does not allow SHA-1 it does
|
|
not make sense to assign SHA-1 as implicit algorithm.
|
|
Instead it is better to use SHA-256 as implicit algorithm
|
|
(which will be the case for rfc4880bis anyway). */
|
|
|
|
if (opt.compliance == CO_DE_VS)
|
|
implicit = DIGEST_ALGO_SHA256;
|
|
else
|
|
implicit = DIGEST_ALGO_SHA1;
|
|
|
|
break;
|
|
|
|
case PREFTYPE_ZIP:
|
|
/* Uncompressed is always an option. */
|
|
implicit=COMPRESS_ALGO_NONE;
|
|
}
|
|
|
|
if (pkr->pk->user_id) /* selected by user ID */
|
|
prefs = pkr->pk->user_id->prefs;
|
|
else
|
|
prefs = pkr->pk->prefs;
|
|
|
|
if( prefs )
|
|
{
|
|
for (i=0; prefs[i].type; i++ )
|
|
{
|
|
if( prefs[i].type == preftype )
|
|
{
|
|
/* Make sure all scores don't add up past 0xFFFF
|
|
(and roll around) */
|
|
if(rank+scores[prefs[i].value]<=0xFFFF)
|
|
scores[prefs[i].value]+=rank;
|
|
else
|
|
scores[prefs[i].value]=0xFFFF;
|
|
|
|
mask[prefs[i].value/32] |= 1<<(prefs[i].value%32);
|
|
|
|
rank++;
|
|
|
|
/* We saw the implicit algorithm, so we don't need
|
|
tack it on the end ourselves. */
|
|
if(implicit==prefs[i].value)
|
|
implicit=-1;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(rank==1 && preftype==PREFTYPE_ZIP)
|
|
{
|
|
/* If the compression preferences are not present, they are
|
|
assumed to be ZIP, Uncompressed (RFC4880:13.3.1) */
|
|
scores[1]=1; /* ZIP is first choice */
|
|
scores[0]=2; /* Uncompressed is second choice */
|
|
mask[0]|=3;
|
|
}
|
|
|
|
/* If the key didn't have the implicit algorithm listed
|
|
explicitly, add it here at the tail of the list. */
|
|
if(implicit>-1)
|
|
{
|
|
scores[implicit]+=rank;
|
|
mask[implicit/32] |= 1<<(implicit%32);
|
|
}
|
|
|
|
for(i=0;i<8;i++)
|
|
bits[i]&=mask[i];
|
|
}
|
|
|
|
/* We've now scored all of the algorithms, and the usable ones have
|
|
bits set. Let's pick the winner. */
|
|
|
|
/* The caller passed us a request. Can we use it? */
|
|
if(request>-1 && (bits[request/32] & (1<<(request%32))) &&
|
|
algo_available(preftype,request,hint))
|
|
result=request;
|
|
|
|
if(result==-1)
|
|
{
|
|
/* If we have personal prefs set, use them. */
|
|
prefs=NULL;
|
|
if(preftype==PREFTYPE_SYM && opt.personal_cipher_prefs)
|
|
prefs=opt.personal_cipher_prefs;
|
|
else if(preftype==PREFTYPE_HASH && opt.personal_digest_prefs)
|
|
prefs=opt.personal_digest_prefs;
|
|
else if(preftype==PREFTYPE_ZIP && opt.personal_compress_prefs)
|
|
prefs=opt.personal_compress_prefs;
|
|
|
|
if( prefs )
|
|
for(i=0; prefs[i].type; i++ )
|
|
{
|
|
if(bits[prefs[i].value/32] & (1<<(prefs[i].value%32))
|
|
&& algo_available( preftype, prefs[i].value, hint))
|
|
{
|
|
result = prefs[i].value;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(result==-1)
|
|
{
|
|
unsigned int best=-1;
|
|
|
|
/* At this point, we have not selected an algorithm due to a
|
|
special request or via personal prefs. Pick the highest
|
|
ranked algorithm (i.e. the one with the lowest score). */
|
|
|
|
if(preftype==PREFTYPE_HASH && scores[DIGEST_ALGO_MD5])
|
|
{
|
|
/* "If you are building an authentication system, the recipient
|
|
may specify a preferred signing algorithm. However, the
|
|
signer would be foolish to use a weak algorithm simply
|
|
because the recipient requests it." (RFC4880:14). If any
|
|
other hash algorithm is available, pretend that MD5 isn't.
|
|
Note that if the user intentionally chose MD5 by putting it
|
|
in their personal prefs, then we do what the user said (as we
|
|
never reach this code). */
|
|
|
|
for(i=DIGEST_ALGO_MD5+1;i<256;i++)
|
|
if(scores[i])
|
|
{
|
|
scores[DIGEST_ALGO_MD5]=0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
for(i=0;i<256;i++)
|
|
{
|
|
/* Note the '<' here. This means in case of a tie, we will
|
|
favor the lower algorithm number. We have a choice
|
|
between the lower number (probably an older algorithm
|
|
with more time in use), or the higher number (probably a
|
|
newer algorithm with less time in use). Older is
|
|
probably safer here, even though the newer algorithms
|
|
tend to be "stronger". */
|
|
if(scores[i] && scores[i]<best
|
|
&& (bits[i/32] & (1<<(i%32)))
|
|
&& algo_available(preftype,i,hint))
|
|
{
|
|
best=scores[i];
|
|
result=i;
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* Select the MDC flag from the pk_list. We can only use MDC if all
|
|
* recipients support this feature.
|
|
*/
|
|
int
|
|
select_mdc_from_pklist (PK_LIST pk_list)
|
|
{
|
|
PK_LIST pkr;
|
|
|
|
if ( !pk_list )
|
|
return 0;
|
|
|
|
for (pkr = pk_list; pkr; pkr = pkr->next)
|
|
{
|
|
int mdc;
|
|
|
|
if (pkr->pk->user_id) /* selected by user ID */
|
|
mdc = pkr->pk->user_id->flags.mdc;
|
|
else
|
|
mdc = pkr->pk->flags.mdc;
|
|
if (!mdc)
|
|
return 0; /* At least one recipient does not support it. */
|
|
}
|
|
return 1; /* Can be used. */
|
|
}
|
|
|
|
|
|
/* Select the AEAD flag from the pk_list. We can only use AEAD if all
|
|
* recipients support this feature. Returns the AEAD to be used or 0
|
|
* if AEAD shall not be used. */
|
|
aead_algo_t
|
|
select_aead_from_pklist (PK_LIST pk_list)
|
|
{
|
|
pk_list_t pkr;
|
|
int aead;
|
|
|
|
if (!pk_list)
|
|
return 0;
|
|
|
|
for (pkr = pk_list; pkr; pkr = pkr->next)
|
|
{
|
|
if (pkr->pk->user_id) /* selected by user ID */
|
|
aead = pkr->pk->user_id->flags.aead;
|
|
else
|
|
aead = pkr->pk->flags.aead;
|
|
if (!aead)
|
|
return 0; /* At least one recipient does not support it. */
|
|
}
|
|
|
|
return AEAD_ALGO_OCB; /* Yes, AEAD can be used. */
|
|
}
|
|
|
|
|
|
/* Print a warning for all keys in PK_LIST missing the AEAD feature
|
|
* flag or AEAD algorithms. */
|
|
void
|
|
warn_missing_aead_from_pklist (PK_LIST pk_list)
|
|
{
|
|
PK_LIST pkr;
|
|
|
|
for (pkr = pk_list; pkr; pkr = pkr->next)
|
|
{
|
|
int mdc;
|
|
|
|
if (pkr->pk->user_id) /* selected by user ID */
|
|
mdc = pkr->pk->user_id->flags.aead;
|
|
else
|
|
mdc = pkr->pk->flags.aead;
|
|
if (!mdc)
|
|
log_info (_("Note: key %s has no %s feature\n"),
|
|
keystr_from_pk (pkr->pk), "AEAD");
|
|
}
|
|
}
|
|
|
|
void
|
|
warn_missing_aes_from_pklist (PK_LIST pk_list)
|
|
{
|
|
PK_LIST pkr;
|
|
|
|
for (pkr = pk_list; pkr; pkr = pkr->next)
|
|
{
|
|
const prefitem_t *prefs;
|
|
int i;
|
|
int gotit = 0;
|
|
|
|
prefs = pkr->pk->user_id? pkr->pk->user_id->prefs : pkr->pk->prefs;
|
|
if (prefs)
|
|
{
|
|
for (i=0; !gotit && prefs[i].type; i++ )
|
|
if (prefs[i].type == PREFTYPE_SYM
|
|
&& prefs[i].value == CIPHER_ALGO_AES)
|
|
gotit++;
|
|
}
|
|
if (!gotit)
|
|
log_info (_("Note: key %s has no preference for %s\n"),
|
|
keystr_from_pk (pkr->pk), "AES");
|
|
}
|
|
}
|