/* pkclist.c * Copyright (C) 1998 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 2 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, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ #include #include #include #include #include #include #include "options.h" #include "packet.h" #include "errors.h" #include "keydb.h" #include "memory.h" #include "util.h" #include "trustdb.h" #include "ttyio.h" #include "status.h" #include "i18n.h" #define CONTROL_D ('D' - 'A' + 1) static void show_paths( ulong lid, int only_first ) { void *context = NULL; unsigned otrust, validity; int last_level, level; last_level = 0; while( (level=enum_cert_paths( &context, &lid, &otrust, &validity)) != -1){ char *p; int rc; size_t n; u32 keyid[2]; PKT_public_key *pk ; if( level < last_level && only_first ) break; last_level = level; rc = keyid_from_lid( lid, keyid ); if( rc ) { log_error("ooops: can't get keyid for lid %lu\n", lid); return; } pk = m_alloc_clear( sizeof *pk ); rc = get_pubkey( pk, keyid ); if( rc ) { log_error("key %08lX: public key not found: %s\n", (ulong)keyid[1], g10_errstr(rc) ); return; } tty_printf("%*s%4u%c/%08lX.%lu %s \"", level*2, "", nbits_from_pk( pk ), pubkey_letter( pk->pubkey_algo ), (ulong)keyid[1], lid, datestr_from_pk( pk ) ); #if 0 c = trust_letter(otrust); if( c ) putchar( c ); else printf( "%02x", otrust ); putchar('/'); c = trust_letter(validity); if( c ) putchar( c ); else printf( "%02x", validity ); putchar(' '); #endif p = get_user_id( keyid, &n ); tty_print_string( p, n ), m_free(p); tty_printf("\"\n"); free_public_key( pk ); } enum_cert_paths( &context, NULL, NULL, NULL ); /* release context */ tty_printf("\n"); } /**************** * Returns true if an ownertrust has changed. */ int edit_ownertrust( ulong lid, int mode ) { char *p; int rc; size_t n; u32 keyid[2]; PKT_public_key *pk ; int changed=0; int quit=0; rc = keyid_from_lid( lid, keyid ); if( rc ) { log_error("ooops: can't get keyid for lid %lu\n", lid); return 0; } pk = m_alloc_clear( sizeof *pk ); rc = get_pubkey( pk, keyid ); if( rc ) { log_error("key %08lX: public key not found: %s\n", (ulong)keyid[1], g10_errstr(rc) ); return 0; } if( !mode ) { tty_printf(_("No trust value assigned to %lu:\n" "%4u%c/%08lX %s \""), lid, nbits_from_pk( pk ), pubkey_letter( pk->pubkey_algo ), (ulong)keyid[1], datestr_from_pk( pk ) ); p = get_user_id( keyid, &n ); tty_print_string( p, n ), m_free(p); tty_printf("\"\n\n"); } tty_printf(_( "Please decide how far you trust this user to correctly\n" "verify other users' keys (by looking at passports,\n" "checking fingerprints from different sources...)?\n\n" " 1 = Don't know\n" " 2 = I do NOT trust\n" " 3 = I trust marginally\n" " 4 = I trust fully\n" " s = please show me more information\n") ); if( mode ) tty_printf(_(" m = back to the main menu\n")); else tty_printf(_(" q = quit\n")); tty_printf("\n"); for(;;) { /* a string with valid answers */ char *ans = _("sSmMqQ"); if( strlen(ans) != 6 ) BUG(); p = cpr_get("edit_ownertrust.value",_("Your decision? ")); trim_spaces(p); cpr_kill_prompt(); if( *p && p[1] ) ; else if( !p[1] && (*p >= '1' && *p <= '4') ) { unsigned 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; default: BUG(); } if( !update_ownertrust( lid, trust ) ) changed++; break; } else if( *p == ans[0] || *p == ans[1] ) { tty_printf(_( "Certificates leading to an ultimately trusted key:\n")); show_paths( lid, 1 ); } else if( mode && (*p == ans[2] || *p == ans[3] || *p == CONTROL_D ) ) { break ; /* back to the menu */ } else if( !mode && (*p == ans[4] || *p == ans[5] ) ) { quit = 1; break ; /* back to the menu */ } m_free(p); p = NULL; } m_free(p); m_free(pk); return quit? -1 : changed; } /**************** * Try to add some more owner trusts (interactive) * This function presents all the signator in a certificate * chain who have no trust value assigned. * Returns: -1 if no ownertrust were added. */ static int add_ownertrust( PKT_public_key *pk, int *quit ) { int rc; void *context = NULL; ulong lid; unsigned otrust, validity; int any=0, changed=0, any_undefined=0; *quit = 0; tty_printf( _("Could not find a valid trust path to the key. Let's see whether we\n" "can assign some missing owner trust values.\n\n")); rc = query_trust_record( pk ); if( rc ) { log_error("Ooops: not in trustdb\n"); return -1; } lid = pk->local_id; while( enum_cert_paths( &context, &lid, &otrust, &validity ) != -1 ) { if( lid == pk->local_id ) continue; any=1; if( changed ) { /* because enum_cert_paths() makes a snapshop of the * trust paths, the otrust and validity are not anymore * valid after changing an entry - we have to reread * those values from then on */ otrust = get_ownertrust( lid ); } if( otrust == TRUST_UNDEFINED ) { any_undefined=1; enum_cert_paths_print( &context, NULL, changed, lid ); tty_printf("\n"); rc = edit_ownertrust( lid, 0 ); if( rc == -1 ) { *quit = 1; break; } else if( rc > 0 ) changed = 1; } } enum_cert_paths( &context, NULL, NULL, NULL ); /* release context */ if( !any ) tty_printf(_("No path leading to one of our keys found.\n\n") ); else if( !any_undefined ) tty_printf(_("No certificates with undefined trust found.\n\n") ); else if( !changed ) tty_printf(_("No trust values changed.\n\n") ); return changed? 0:-1; } /**************** * 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, int trustlevel ) { int rc; if( (trustlevel & TRUST_FLAG_REVOKED) ) { log_info(_("key %08lX: key has been revoked!\n"), (ulong)keyid_from_pk( pk, NULL) ); if( opt.batch ) return 0; if( !cpr_get_answer_is_yes("revoked_key.override", _("Use this key anyway? ")) ) return 0; } switch( (trustlevel & TRUST_MASK) ) { case TRUST_UNKNOWN: /* No pubkey in trustDB: Insert and check again */ rc = insert_trust_record( pk ); if( rc ) { log_error("failed to insert it into the trustdb: %s\n", g10_errstr(rc) ); return 0; /* no */ } rc = check_trust( pk, &trustlevel ); if( rc ) log_fatal("trust check after insert failed: %s\n", g10_errstr(rc) ); if( trustlevel == TRUST_UNKNOWN || trustlevel == TRUST_EXPIRED ) BUG(); return do_we_trust( pk, trustlevel ); case TRUST_EXPIRED: log_info(_("%08lX: key has expired\n"), (ulong)keyid_from_pk( pk, NULL) ); return 0; /* no */ case TRUST_UNDEFINED: if( opt.batch || opt.answer_no ) log_info(_("%08lX: no info to calculate a trust probability\n"), (ulong)keyid_from_pk( pk, NULL) ); else { int quit; rc = add_ownertrust( pk, &quit ); if( !rc && !quit ) { rc = check_trust( pk, &trustlevel ); if( rc ) log_fatal("trust check after add_ownertrust failed: %s\n", g10_errstr(rc) ); /* fixme: this is recursive; we should unroll it */ return do_we_trust( pk, trustlevel ); } } return 0; case TRUST_NEVER: log_info(_("%08lX: We do NOT trust this key\n"), (ulong)keyid_from_pk( pk, NULL) ); return 0; /* no */ case TRUST_MARGINAL: log_info( _("%08lX: It is not sure that this key really belongs to the owner\n" "but it is accepted anyway\n"), (ulong)keyid_from_pk( pk, NULL) ); return 1; /* yes */ case TRUST_FULLY: if( opt.verbose ) log_info(_("This key probably belongs to the owner\n")); return 1; /* yes */ case TRUST_ULTIMATE: if( opt.verbose ) log_info(_("This key belongs to us\n")); return 1; /* yes */ default: BUG(); } /* Eventuell fragen falls der trustlevel nicht ausreichend ist */ return 1; /* yes */ } /**************** * wrapper around do_we_trust, so we can ask whether to use the * key anyway. */ static int do_we_trust_pre( PKT_public_key *pk, int trustlevel ) { int rc = do_we_trust( pk, trustlevel ); if( (trustlevel & TRUST_FLAG_REVOKED) && !rc ) return 0; else if( !opt.batch && !rc ) { tty_printf(_( "It is NOT certain that the key belongs to its owner.\n" "If you *really* know what you are doing, you may answer\n" "the next question with yes\n\n") ); if( cpr_get_answer_is_yes("untrusted_key.override", _("Use this key anyway? ")) ) rc = 1; } else if( opt.always_trust && !rc ) { log_info(_("WARNING: Using untrusted key!\n")); rc = 1; } return rc; } /**************** * Check whether we can trust this signature. * Returns: Error if we shall not trust this signatures. */ int check_signatures_trust( PKT_signature *sig ) { PKT_public_key *pk = m_alloc_clear( sizeof *pk ); int trustlevel; int dont_try = 0; int rc=0; rc = get_pubkey( pk, sig->keyid ); if( rc ) { /* this should not happen */ log_error("Ooops; the key vanished - can't check the trust\n"); rc = G10ERR_NO_PUBKEY; goto leave; } retry: rc = check_trust( pk, &trustlevel ); if( rc ) { log_error("check trust failed: %s\n", g10_errstr(rc)); goto leave; } if( (trustlevel & TRUST_FLAG_REVOKED) ) { write_status( STATUS_KEYREVOKED ); log_info(_("WARNING: This key has been revoked by its owner!\n")); log_info(_(" This could mean that the signature is forgery.\n")); } switch( (trustlevel & TRUST_MASK) ) { case TRUST_UNKNOWN: /* No pubkey in trustDB: Insert and check again */ rc = insert_trust_record( pk ); if( rc ) { log_error("failed to insert it into the trustdb: %s\n", g10_errstr(rc) ); goto leave; } rc = check_trust( pk, &trustlevel ); if( rc ) log_fatal("trust check after insert failed: %s\n", g10_errstr(rc) ); if( trustlevel == TRUST_UNKNOWN || trustlevel == TRUST_EXPIRED ) BUG(); goto retry; case TRUST_EXPIRED: log_info(_("Note: This key has expired!\n")); break; case TRUST_UNDEFINED: if( dont_try || opt.batch || opt.answer_no ) { write_status( STATUS_TRUST_UNDEFINED ); 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" )); } else { int quit; rc = add_ownertrust( pk, &quit ); if( rc || quit ) { dont_try = 1; rc = 0; } goto retry; } break; case TRUST_NEVER: write_status( STATUS_TRUST_NEVER ); log_info(_("WARNING: We do NOT trust this key!\n")); log_info(_(" The signature is probably a FORGERY.\n")); rc = G10ERR_BAD_SIGN; break; case TRUST_MARGINAL: write_status( STATUS_TRUST_MARGINAL ); 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" )); break; case TRUST_FULLY: write_status( STATUS_TRUST_FULLY ); break; case TRUST_ULTIMATE: write_status( STATUS_TRUST_ULTIMATE ); break; default: BUG(); } leave: free_public_key( pk ); return rc; } void release_pk_list( PK_LIST pk_list ) { PK_LIST pk_rover; for( ; pk_list; pk_list = pk_rover ) { pk_rover = pk_list->next; free_public_key( pk_list->pk ); m_free( pk_list ); } } int build_pk_list( STRLIST remusr, PK_LIST *ret_pk_list, unsigned use ) { PK_LIST pk_list = NULL; PKT_public_key *pk=NULL; int rc=0; int any_recipients=0; STRLIST rov; /* 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 & 1) ) any_recipients = 1; else if( (use & PUBKEY_USAGE_ENC) && !opt.no_encrypt_to ) { pk = m_alloc_clear( sizeof *pk ); pk->pubkey_usage = use; if( (rc = get_pubkey_byname( NULL, pk, rov->d, NULL )) ) { free_public_key( pk ); pk = NULL; log_error(_("%s: skipped: %s\n"), rov->d, g10_errstr(rc) ); } else if( !(rc=check_pubkey_algo2(pk->pubkey_algo, use )) ) { PK_LIST r; r = m_alloc( sizeof *r ); r->pk = pk; pk = NULL; r->next = pk_list; r->mark = 0; pk_list = r; } else { free_public_key( pk ); pk = NULL; log_error(_("%s: skipped: %s\n"), rov->d, g10_errstr(rc) ); } } } if( !any_recipients && !opt.batch ) { /* ask */ char *answer=NULL; tty_printf(_( "You did not specify a user ID. (you may use \"-r\")\n\n")); for(;;) { rc = 0; m_free(answer); answer = cpr_get_utf8("pklist.user_id.enter", _("Enter the user ID: ")); trim_spaces(answer); cpr_kill_prompt(); if( !*answer ) break; if( pk ) free_public_key( pk ); pk = m_alloc_clear( sizeof *pk ); pk->pubkey_usage = use; rc = get_pubkey_byname( NULL, pk, answer, NULL ); if( rc ) tty_printf(_("No such user ID.\n")); else if( !(rc=check_pubkey_algo2(pk->pubkey_algo, use)) ) { int trustlevel; rc = check_trust( pk, &trustlevel ); if( rc ) { log_error("error checking pk of `%s': %s\n", answer, g10_errstr(rc) ); } else if( do_we_trust_pre( pk, trustlevel ) ) { PK_LIST r; r = m_alloc( sizeof *r ); r->pk = pk; pk = NULL; r->next = pk_list; r->mark = 0; pk_list = r; any_recipients = 1; break; } } } m_free(answer); if( pk ) { free_public_key( pk ); pk = NULL; } } else { for(; remusr; remusr = remusr->next ) { if( (remusr->flags & 1) ) continue; /* encrypt-to keys are already handled */ pk = m_alloc_clear( sizeof *pk ); pk->pubkey_usage = use; if( (rc = get_pubkey_byname( NULL, pk, remusr->d, NULL )) ) { free_public_key( pk ); pk = NULL; log_error(_("%s: skipped: %s\n"), remusr->d, g10_errstr(rc) ); } else if( !(rc=check_pubkey_algo2(pk->pubkey_algo, use )) ) { int trustlevel; rc = check_trust( pk, &trustlevel ); if( rc ) { free_public_key( pk ); pk = NULL; log_error(_("%s: error checking key: %s\n"), remusr->d, g10_errstr(rc) ); } else if( do_we_trust_pre( pk, trustlevel ) ) { /* note: do_we_trust may have changed the trustlevel */ PK_LIST r; r = m_alloc( sizeof *r ); r->pk = pk; pk = NULL; r->next = pk_list; r->mark = 0; pk_list = r; any_recipients = 1; } else { /* we don't trust this pk */ free_public_key( pk ); pk = NULL; } } else { free_public_key( pk ); pk = NULL; log_error(_("%s: skipped: %s\n"), remusr->d, g10_errstr(rc) ); } } } if( !rc && !any_recipients ) { log_error(_("no valid addressees\n")); rc = G10ERR_NO_USER_ID; } if( rc ) release_pk_list( pk_list ); else *ret_pk_list = pk_list; return rc; } /**************** * Return -1 if we could not find an algorithm. */ int select_algo_from_prefs( PK_LIST pk_list, int preftype ) { PK_LIST pkr; u32 bits[8]; byte *pref = NULL; size_t npref; int i, j; int compr_hack=0; int any; if( !pk_list ) return -1; memset( bits, ~0, 8 * sizeof *bits ); for( pkr = pk_list; pkr; pkr = pkr->next ) { u32 mask[8]; memset( mask, 0, 8 * sizeof *mask ); if( !pkr->pk->local_id ) { /* try to set the local id */ query_trust_info( pkr->pk ); if( !pkr->pk->local_id ) { log_debug("select_algo_from_prefs: can't get LID\n"); continue; } } if( preftype == PREFTYPE_SYM ) mask[0] |= (1<<2); /* 3DES is implicitly there */ m_free(pref); pref = get_pref_data( pkr->pk->local_id, pkr->pk->namehash, &npref); any = 0; if( pref ) { #if 0 log_hexdump("raw: ", pref, npref ); #endif for(i=0; i+1 < npref; i+=2 ) { if( pref[i] == preftype ) { mask[pref[i+1]/32] |= 1 << (pref[i+1]%32); any = 1; } } } if( (!pref || !any) && preftype == PREFTYPE_COMPR ) { mask[0] |= 3; /* asume no_compression and old pgp */ compr_hack = 1; } #if 0 log_debug("mask=%08lX%08lX%08lX%08lX%08lX%08lX%08lX%08lX\n", (ulong)mask[7], (ulong)mask[6], (ulong)mask[5], (ulong)mask[4], (ulong)mask[3], (ulong)mask[2], (ulong)mask[1], (ulong)mask[0]); #endif for(i=0; i < 8; i++ ) bits[i] &= mask[i]; #if 0 log_debug("bits=%08lX%08lX%08lX%08lX%08lX%08lX%08lX%08lX\n", (ulong)bits[7], (ulong)bits[6], (ulong)bits[5], (ulong)bits[4], (ulong)bits[3], (ulong)bits[2], (ulong)bits[1], (ulong)bits[0]); #endif } /* usable algorithms are now in bits * We now use the last key from pk_list to select * the algorithm we want to use. there are no * preferences for the last key, we select the one * corresponding to first set bit. */ i = -1; any = 0; if( pref ) { for(j=0; j+1 < npref; j+=2 ) { if( pref[j] == preftype ) { any = 1; if( (bits[pref[j+1]/32] & (1<<(pref[j+1]%32))) ) { /* fixme: check whether this algoritm is available */ i = pref[j+1]; break; } } } } if( !pref || !any ) { for(j=0; j < 256; j++ ) if( (bits[j/32] & (1<<(j%32))) ) { /* fixme: check whether this algoritm is available */ i = j; break; } } #if 0 log_debug("prefs of type %d: selected %d\n", preftype, i ); #endif if( compr_hack && !i ) { /* selected no compression, but we should check whether * algorithm 1 is also available (the ordering is not relevant * in this case). */ if( bits[0] & (1<<1) ) i = 1; /* yep; we can use compression algo 1 */ } m_free(pref); return i; }