diff options
Diffstat (limited to '')
-rw-r--r-- | g10/pkclist.c | 1721 |
1 files changed, 1721 insertions, 0 deletions
diff --git a/g10/pkclist.c b/g10/pkclist.c new file mode 100644 index 0000000..fb8b176 --- /dev/null +++ b/g10/pkclist.c @@ -0,0 +1,1721 @@ +/* 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. + * + * 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/>. + */ + +#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 "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->hashed, 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_byfprint (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) + 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_pubkey_info (ctrl, NULL,pk); + 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 inclduing the validation model. */ +static void +write_trust_status (int statuscode, int trustlevel) +{ +#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; + write_status_strings (statuscode, "0 ", trust_model_string (tm), NULL); +#endif /* NO_TRUST_MODELS */ +} + + +/**************** + * 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) +{ + PKT_public_key *pk = xmalloc_clear( sizeof *pk ); + unsigned int trustlevel = TRUST_UNKNOWN; + int rc=0; + + 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.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_info(_("WARNING: this key might be revoked (revocation key" + " not present)\n")); + + trustlevel = get_validity (ctrl, NULL, pk, NULL, sig, 1); + + if ( (trustlevel & TRUST_FLAG_REVOKED) ) + { + write_status( STATUS_KEYREVOKED ); + if(pk->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")); + + /* If we have PKA information adjust the trustlevel. */ + if (sig->pka_info && sig->pka_info->valid) + { + 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); + free_public_key (primary_pk); + + if ( fprlen == 20 && !memcmp (sig->pka_info->fpr, fpr, 20) ) + { + okay = 1; + write_status_text (STATUS_PKA_TRUST_GOOD, sig->pka_info->email); + log_info (_("Note: Verified signer's address is '%s'\n"), + sig->pka_info->email); + } + else + { + okay = 0; + write_status_text (STATUS_PKA_TRUST_BAD, sig->pka_info->email); + log_info (_("Note: Signer's address '%s' " + "does not match DNS entry\n"), sig->pka_info->email); + } + + switch ( (trustlevel & TRUST_MASK) ) + { + case TRUST_UNKNOWN: + case TRUST_UNDEFINED: + case TRUST_MARGINAL: + if (okay && opt.verify_options&VERIFY_PKA_TRUST_INCREASE) + { + trustlevel = ((trustlevel & ~TRUST_MASK) | TRUST_FULLY); + log_info (_("trustlevel adjusted to FULL" + " due to valid PKA info\n")); + } + /* fall through */ + case TRUST_FULLY: + if (!okay) + { + trustlevel = ((trustlevel & ~TRUST_MASK) | TRUST_NEVER); + log_info (_("trustlevel adjusted to NEVER" + " due to bad PKA info\n")); + } + break; + } + } + + /* 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); + 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); + 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); + rc = 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" + " 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); + if (opt.with_fingerprint) + print_fingerprint (ctrl, NULL, pk, 1); + break; + + case TRUST_ULTIMATE: + write_trust_status (STATUS_TRUST_ULTIMATE, trustlevel); + if (opt.with_fingerprint) + print_fingerprint (ctrl, NULL, pk, 1); + break; + } + + leave: + free_public_key( pk ); + return rc; +} + + +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; +} + + +static int +expand_id(const char *id,strlist_t *into,unsigned int flags) +{ + struct groupitem *groups; + int count=0; + + for(groups=opt.grouplist;groups;groups=groups->next) + { + /* need strcasecmp() here, as this should be localized */ + if(strcasecmp(groups->name,id)==0) + { + strlist_t each,sl; + + /* this maintains the current utf8-ness */ + for(each=groups->values;each;each=each->next) + { + sl=add_to_strlist(into,each->d); + sl->flags=flags; + count++; + } + + break; + } + } + + return count; +} + +/* For simplicity, and to avoid potential loops, we only expand once - + * you can't make an alias that points to an alias. */ +static strlist_t +expand_group (strlist_t input) +{ + strlist_t output = NULL; + strlist_t sl, rover; + + for (rover = input; rover; rover = rover->next) + if (!(rover->flags & PK_LIST_FROM_FILE) + && !expand_id(rover->d,&output,rover->flags)) + { + /* Didn't find any groups, so use the existing string */ + sl=add_to_strlist(&output,rover->d); + sl->flags=rover->flags; + } + + return output; +} + + +/* 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 keyblock = NULL; + + 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); + 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); + release_kbnode (keyblock); + if ( (trustlevel & TRUST_FLAG_DISABLED) ) + { + /* Key has been disabled. */ + 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. */ + 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 = xtrymalloc (sizeof *r); + if (!r) + { + rc = gpg_error_from_syserror (); + free_public_key (pk); + return rc; + } + r->pk = pk; + r->next = *pk_list_addr; + r->flags = mark_hidden? 1:0; + *pk_list_addr = r; + } + + 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); + 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) && (PGP6 || 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) && (PGP6 || 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(PGP6 && (algo != CIPHER_ALGO_IDEA + && algo != CIPHER_ALGO_3DES + && algo != CIPHER_ALGO_CAST5)) + return 0; + + 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((PGP6 || 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((PGP6 || 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: + /* 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. + + Because "de-vs" compliance will soon not anymore allow + 3DES it does not make sense to assign 3DES as implicit + algorithm. Instead it is better to use AES-128 as + implicit algorithm here. */ + if (opt.compliance == CO_DE_VS) + implicit = CIPHER_ALGO_AES; + else + implicit=CIPHER_ALGO_3DES; + + 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. */ +} + + +/* Print a warning for all keys in PK_LIST missing the MDC feature. */ +void +warn_missing_mdc_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.mdc; + else + mdc = pkr->pk->flags.mdc; + if (!mdc) + log_info (_("Note: key %s has no %s feature\n"), + keystr_from_pk (pkr->pk), "MDC"); + } +} + +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"); + } +} |