diff --git a/g10/ChangeLog b/g10/ChangeLog index dea2de16f..286ae8423 100644 --- a/g10/ChangeLog +++ b/g10/ChangeLog @@ -1,5 +1,35 @@ 2002-10-29 David Shaw + * packet.h, trustdb.h, trustdb.c (trust_string): New. Return a + string like "fully trusted", "marginally trusted", etc. + (get_min_ownertrust): New. Return minimum ownertrust. + (update_min_ownertrust): New. Set minimum ownertrust. + (check_regexp): New. Check a regular epression against a user ID. + (ask_ownertrust): Allow specifying a minimum value. + (get_ownertrust_info): Follow the minimum ownertrust when + returning a letter. + (clear_validity): Remove minimum ownertrust when a key becomes + invalid. + (release_key_items): Release regexp along with the rest of the + info. + (validate_one_keyblock, validate_keys): Build a trust sig chain + while validating. Call check_regexp for regexps. Use the minimum + ownertrust if the user does not specify a genuine ownertrust. + + * pkclist.c (do_edit_ownertrust): Only allow user to select a + trust level greater than the minimum value. + + * parse-packet.c (can_handle_critical): Can handle critical trust + and regexp subpackets. + + * trustdb.h, trustdb.c (clear_ownertrusts), delkey.c + (do_delete_key), import.c (import_one): Rename clear_ownertrust to + clear_ownertrusts and have it clear the min_ownertrust value as + well. + + * keylist.c (list_keyblock_print): Indent uid to match pub and + sig. + * keyedit.c (print_and_check_one_sig, show_key_and_fingerprint, menu_addrevoker), keylist.c (list_keyblock_print, print_fingerprint): Show "T" or the trust depth for trust diff --git a/g10/delkey.c b/g10/delkey.c index e4cada3f5..35c903cc0 100644 --- a/g10/delkey.c +++ b/g10/delkey.c @@ -161,7 +161,7 @@ do_delete_key( const char *username, int secret, int *r_sec_avail ) revalidation_mark(). This makes sense - only deleting keys that have ownertrust set should trigger this. */ - if (!secret && pk && clear_ownertrust (pk)) { + if (!secret && pk && clear_ownertrusts (pk)) { if (opt.verbose) log_info (_("ownertrust information cleared\n")); } diff --git a/g10/import.c b/g10/import.c index abea39223..2ff06b51d 100644 --- a/g10/import.c +++ b/g10/import.c @@ -671,7 +671,7 @@ import_one( const char *fname, KBNODE keyblock, the keyring and trustdb are out of sync. It can also be made to happen with the trusted-key command. */ - clear_ownertrust (pk); + clear_ownertrusts (pk); revalidation_mark (); } keydb_release (hd); diff --git a/g10/keylist.c b/g10/keylist.c index b45bfca0f..afe88265d 100644 --- a/g10/keylist.c +++ b/g10/keylist.c @@ -495,7 +495,7 @@ list_keyblock_print ( KBNODE keyblock, int secret, int fpr, void *opaque ) continue; if( any ) - printf("uid%*s", 28, ""); + printf("uid%*s", 29, ""); if ( node->pkt->pkt.user_id->is_revoked ) fputs ("[revoked] ", stdout); diff --git a/g10/packet.h b/g10/packet.h index a2e710c4c..c3dc0c7ab 100644 --- a/g10/packet.h +++ b/g10/packet.h @@ -212,6 +212,10 @@ typedef struct { PKT_user_id *user_id; /* if != NULL: found by that uid */ struct revocation_key *revkey; int numrevkeys; + u32 trust_timestamp; + byte trust_depth; + byte trust_value; + const byte *trust_regexp; MPI pkey[PUBKEY_MAX_NPKEY]; } PKT_public_key; diff --git a/g10/parse-packet.c b/g10/parse-packet.c index f15f412ef..ee7091ecc 100644 --- a/g10/parse-packet.c +++ b/g10/parse-packet.c @@ -780,7 +780,7 @@ dump_sig_subpkt( int hashed, int type, int critical, if(length!=2) p="[invalid trust subpacket]"; else - printf("trust signature of depth %d, amount %d",buffer[0],buffer[1]); + printf("trust signature of depth %d, value %d",buffer[0],buffer[1]); break; case SIGSUBPKT_REGEXP: if(!length) @@ -1002,6 +1002,8 @@ can_handle_critical( const byte *buffer, size_t n, int type ) case SIGSUBPKT_PRIMARY_UID: case SIGSUBPKT_FEATURES: case SIGSUBPKT_POLICY: /* Is it enough to show the policy? */ + case SIGSUBPKT_TRUST: + case SIGSUBPKT_REGEXP: return 1; default: @@ -1300,6 +1302,8 @@ parse_signature( IOBUF inp, int pkttype, unsigned long pktlen, sig->trust_depth=p[0]; sig->trust_value=p[1]; + /* Only look for a regexp if there is also a trust + subpacket. */ sig->trust_regexp= parse_sig_subpkt(sig->hashed,SIGSUBPKT_REGEXP,&len); diff --git a/g10/pkclist.c b/g10/pkclist.c index cb1c506e3..e54d4d535 100644 --- a/g10/pkclist.c +++ b/g10/pkclist.c @@ -245,7 +245,18 @@ do_edit_ownertrust (PKT_public_key *pk, int mode, int changed=0; int quit=0; int show=0; + int min_num; int did_help=defer_help; + unsigned int minimum=get_min_ownertrust(pk); + + switch(minimum) + { + default: min_num=0; break; + 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(;;) { @@ -299,10 +310,14 @@ do_edit_ownertrust (PKT_public_key *pk, int mode, "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")); - tty_printf (_(" %d = Don't know\n"), 1); - tty_printf (_(" %d = I do NOT trust\n"), 2); - tty_printf (_(" %d = I trust marginally\n"), 3); - tty_printf (_(" %d = I trust fully\n"), 4); + if(min_num<=1) + tty_printf (_(" %d = I don't know\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 @@ -317,6 +332,9 @@ do_edit_ownertrust (PKT_public_key *pk, int mode, tty_printf(_(" q = quit\n")); } tty_printf("\n"); + if(minimum) + tty_printf(_("The minimum trust level for this key is: %s\n\n"), + trust_string(minimum)); did_help = 1; } if( strlen(ans) != 8 ) @@ -328,7 +346,7 @@ do_edit_ownertrust (PKT_public_key *pk, int mode, did_help = 0; else if( *p && p[1] ) ; - else if( !p[1] && (*p >= '1' && *p <= (mode?'5':'4')) ) + else if( !p[1] && ((*p >= '0'+min_num) && *p <= (mode?'5':'4')) ) { unsigned int trust; switch( *p ) diff --git a/g10/trustdb.c b/g10/trustdb.c index fe2ad34e3..1a8e74df1 100644 --- a/g10/trustdb.c +++ b/g10/trustdb.c @@ -24,6 +24,15 @@ #include #include +#ifndef DISABLE_REGEX +#include +#ifdef USE_GNU_REGEX +#include "_regex.h" +#else +#include +#endif +#endif /* !DISABLE_REGEX */ + #include "errors.h" #include "iobuf.h" #include "keydb.h" @@ -43,7 +52,10 @@ */ struct key_item { struct key_item *next; - unsigned int ownertrust; + unsigned int ownertrust,min_ownertrust; + byte trust_depth; + byte trust_value; + char *trust_regexp; u32 kid[2]; }; @@ -97,6 +109,7 @@ release_key_items (struct key_item *k) for (; k; k = k2) { k2 = k->next; + m_free (k->trust_regexp); m_free (k); } } @@ -440,6 +453,23 @@ trust_letter (unsigned int value) } } +/* The strings here are similar to those in + pkclist.c:do_edit_ownertrust() */ +const char * +trust_string (unsigned int value) +{ + switch( (value & TRUST_MASK) ) + { + case TRUST_UNKNOWN: return _("unknown trust"); + case TRUST_EXPIRED: return _("expired"); + case TRUST_UNDEFINED: return _("undefined trust"); + case TRUST_NEVER: return _("do NOT trust"); + case TRUST_MARGINAL: return _("marginal trust"); + case TRUST_FULLY: return _("full trust"); + case TRUST_ULTIMATE: return _("ultimate trust"); + default: return "err"; + } +} /**************** * Recreate the WoT but do not ask for new ownertrusts. Special @@ -525,7 +555,6 @@ read_trust_record (PKT_public_key *pk, TRUSTREC *rec) return 0; } - /**************** * Return the assigned ownertrust value for the given public key. * The key should be the primary key. @@ -544,20 +573,50 @@ get_ownertrust ( PKT_public_key *pk) tdbio_invalid (); return rc; /* actually never reached */ } - + return rec.r.trust.ownertrust; } +unsigned int +get_min_ownertrust (PKT_public_key *pk) +{ + TRUSTREC rec; + int rc; + + rc = read_trust_record (pk, &rec); + if (rc == -1) + return TRUST_UNKNOWN; /* no record yet */ + if (rc) + { + tdbio_invalid (); + return rc; /* actually never reached */ + } + + return rec.r.trust.min_ownertrust; +} + /* * Same as get_ownertrust but return a trust letter instead of an value. */ int get_ownertrust_info (PKT_public_key *pk) { - unsigned int otrust; + unsigned int otrust,otrust_min; int c; otrust = get_ownertrust (pk); + otrust_min = get_min_ownertrust (pk); + if(otrust0) ot = get_ownertrust (pk); else if(ot==0) - ot = TRUST_UNDEFINED; + ot = minimum?minimum:TRUST_UNDEFINED; else ot = -1; /* quit */ free_public_key( pk ); + return ot; } @@ -1196,6 +1321,33 @@ mark_usable_uid_certs (KBNODE keyblock, KBNODE uidnode, } } +/* Used by validate_one_keyblock to confirm a regexp within a trust + signature. Returns 1 for match, and 0 for no match or regex + error. */ +static int +check_regexp(const char *exp,const char *string) +{ +#ifdef DISABLE_REGEXP + /* When DISABLE_REGEXP is defined, assume all regexps do not + match. */ + return 0; +#else + int ret; + regex_t pat; + + if(regcomp(&pat,exp,REG_ICASE|REG_NOSUB)!=0) + return 0; + + ret=regexec(&pat,string,0,NULL,0); + + regfree(&pat); + + if(DBG_TRUST) + log_debug("regexp \"%s\" on \"%s\": %s\n",exp,string,ret==0?"YES":"NO"); + + return (ret==0); +#endif +} /* * Return true if the key is signed by one of the keys in the given @@ -1243,19 +1395,72 @@ validate_one_keyblock (KBNODE kb, struct key_item *klist, else if (node->pkt->pkttype == PKT_SIGNATURE && (node->flag & (1<<8)) ) { + /* Note that we are only seeing unrevoked sigs here */ PKT_signature *sig = node->pkt->pkt.signature; kr = is_in_klist (klist, sig); - if (kr) + /* If the trust_regexp does not match, it's as if the sig + did not exist. This is safe for non-trust sigs as well + since we don't accept a regexp on the sig unless it's a + trust sig. */ + if (kr && (kr->trust_regexp==NULL || + (uidnode && check_regexp(kr->trust_regexp, + uidnode->pkt->pkt.user_id->name)))) { - if (kr->ownertrust == TRUST_ULTIMATE) + if(DBG_TRUST && sig->trust_depth) + log_debug("trust sig on %s, sig depth is %d, kr depth is %d\n", + uidnode->pkt->pkt.user_id->name,sig->trust_depth, + kr->trust_depth); + + /* Are we part of a trust sig chain? We always favor + the latest trust sig, rather than the greater or + lesser trust sig or value. I could make a decent + argument for any of these cases, but this seems to be + what PGP does, and I'd like to be compatible. -dms */ + if(sig->trust_depth && + pk->trust_timestamp<=sig->timestamp && + (sig->trust_depth<=kr->trust_depth || + kr->ownertrust==TRUST_ULTIMATE)) + { + /* If we got here, we know that: + + this is a trust sig. + + it's a newer trust sig than any previous trust + sig on this key (not uid). + + it is legal in that it was either generated by an + ultimate key, or a key that was part of a trust + chain, and the depth does not violate the + original trust sig. + + if there is a regexp attached, it matched + successfully. + */ + + if(DBG_TRUST) + log_debug("replacing trust value %d with %d and " + "depth %d with %d\n", + pk->trust_value,sig->trust_value, + pk->trust_depth,sig->trust_depth); + + pk->trust_value=sig->trust_value; + pk->trust_depth=sig->trust_depth-1; + + /* If the trust sig contains a regexp, record it + on the pk for the next round. */ + if(sig->trust_regexp) + pk->trust_regexp=sig->trust_regexp; + } + + if (kr->ownertrust == TRUST_ULTIMATE) fully_count = opt.completes_needed; else if (kr->ownertrust == TRUST_FULLY) fully_count++; else if (kr->ownertrust == TRUST_MARGINAL) marginal_count++; issigned = 1; - } + } } } @@ -1494,7 +1699,6 @@ validate_keys (int interactive) goto leave; } - /* mark all UTKs as visited and set validity to ultimate */ for (k=utk_list; k; k = k->next) { @@ -1532,7 +1736,6 @@ validate_keys (int interactive) do_sync (); } - klist = utk_list; kdb = keydb_new (0); @@ -1544,14 +1747,43 @@ validate_keys (int interactive) ot_marginal = ot_full = ot_ultimate = 0; for (k=klist; k; k = k->next) { + int min=0; + + /* 120 and 60 are as per RFC2440 */ + if(k->trust_value>=120) + min=TRUST_FULLY; + else if(k->trust_value>=60) + min=TRUST_MARGINAL; + + if(min!=k->min_ownertrust) + update_min_ownertrust(k->kid,min); + if (interactive && k->ownertrust == TRUST_UNKNOWN) - k->ownertrust = ask_ownertrust (k->kid); - if (k->ownertrust == -1) { - quit=1; - goto leave; + k->ownertrust = ask_ownertrust (k->kid,min); + + if (k->ownertrust == -1) + { + quit=1; + goto leave; + } } - else if (k->ownertrust == TRUST_UNKNOWN) + + /* This can happen during transition from an old trustdb + before trust sigs. It can also happen if a user uses two + different versions of GnuPG. */ + if(k->ownertrustkid[1], + trust_string(k->ownertrust),trust_string(min)); + + k->ownertrust=min; + } + + if (k->ownertrust == TRUST_UNKNOWN) ot_unknown++; else if (k->ownertrust == TRUST_UNDEFINED) ot_undefined++; @@ -1574,7 +1806,6 @@ validate_keys (int interactive) goto leave; } - for (key_count=0, kar=keys; kar->keyblock; kar++, key_count++) ; @@ -1604,6 +1835,16 @@ validate_keys (int interactive) keyid_from_pk (kar->keyblock->pkt->pkt.public_key, k->kid); k->ownertrust = get_ownertrust (kar->keyblock ->pkt->pkt.public_key); + k->min_ownertrust = get_min_ownertrust (kar->keyblock + ->pkt->pkt.public_key); + k->trust_depth= + kar->keyblock->pkt->pkt.public_key->trust_depth; + k->trust_value= + kar->keyblock->pkt->pkt.public_key->trust_value; + if(kar->keyblock->pkt->pkt.public_key->trust_regexp) + k->trust_regexp= + m_strdup(kar->keyblock->pkt-> + pkt.public_key->trust_regexp); k->next = klist; klist = k; break; @@ -1638,5 +1879,3 @@ validate_keys (int interactive) } return rc; } - - diff --git a/g10/trustdb.h b/g10/trustdb.h index c94a2daa1..8d8a0cf3e 100644 --- a/g10/trustdb.h +++ b/g10/trustdb.h @@ -46,6 +46,7 @@ void init_trustdb( void ); void sync_trustdb( void ); int trust_letter( unsigned value ); +const char *trust_string (unsigned int value); void revalidation_mark (void); @@ -60,9 +61,10 @@ void enum_cert_paths_print( void **context, FILE *fp, int refresh, ulong selected_lid ); unsigned int get_ownertrust (PKT_public_key *pk); +unsigned int get_min_ownertrust (PKT_public_key *pk); int get_ownertrust_info (PKT_public_key *pk); void update_ownertrust (PKT_public_key *pk, unsigned int new_trust ); -int clear_ownertrust (PKT_public_key *pk); +int clear_ownertrusts (PKT_public_key *pk); /*-- tdbdump.c --*/