1
0
Fork 0
mirror of git://git.gnupg.org/gnupg.git synced 2025-07-02 22:46:30 +02:00

g10: Add TOFU support.

* configure.ac: Check for sqlite3.
(SQLITE3_CFLAGS): AC_SUBST it.
(SQLITE3_LIBS): Likewise.
* g10/Makefile.am (AM_CFLAGS): Add $(SQLITE3_CFLAGS).
(gpg2_SOURCES): Add tofu.h and tofu.c.
(gpg2_LDADD): Add $(SQLITE3_LIBS).
* g10/tofu.c: New file.
* g10/tofu.h: New file.
* g10/options.h (trust_model): Define TM_TOFU and TM_TOFU_PGP.
(tofu_db_format): Define.
* g10/packet.h (PKT_signature): Add fields digest and digest_len.
* g10/gpg.c: Include "tofu.h".
(cmd_and_opt_values): Declare aTOFUPolicy, oTOFUDefaultPolicy,
oTOFUDBFormat.
(opts): Add them.
(parse_trust_model): Recognize the tofu and tofu+pgp trust models.
(parse_tofu_policy): New function.
(parse_tofu_db_format): New function.
(main): Initialize opt.tofu_default_policy and opt.tofu_db_format.
Handle aTOFUPolicy, oTOFUDefaultPolicy and oTOFUDBFormat.
* g10/mainproc.c (do_check_sig): If the signature is good, copy the
hash to SIG->DIGEST and set SIG->DIGEST_LEN appropriately.
* g10/trustdb.h (get_validity): Add arguments sig and may_ask.  Update
callers.
(tdb_get_validity_core): Add arguments sig and may_ask.  Update
callers.
* g10/trust.c (get_validity) Add arguments sig and may_ask.  Pass them
to tdb_get_validity_core.
* g10/trustdb.c: Include "tofu.h".
(trust_model_string): Handle TM_TOFU and TM_TOFU_PGP.
(tdb_get_validity_core): Add arguments sig and may_ask.  If
OPT.TRUST_MODEL is TM_TOFU or TM_TOFU_PGP, compute the TOFU trust
level.  Combine it with the computed PGP trust level, if appropriate.
* g10/keyedit.c: Include "tofu.h".
(show_key_with_all_names_colon): If the trust mode is tofu or
tofu+pgp, then show the trust policy.
* g10/keylist.c: Include "tofu.h".
(public_key_list): Also show the PGP stats if the trust model is
TM_TOFU_PGP.
(list_keyblock_colon): If the trust mode is tofu or
tofu+pgp, then show the trust policy.
* g10/pkclist.c: Include "tofu.h".
* g10/gpgv.c (get_validity): Add arguments sig and may_ask.
(enum tofu_policy): Define.
(tofu_get_policy): New stub.
(tofu_policy_str): Likewise.
* g10/test-stubs.c (get_validity): Add arguments sig and may_ask.
(enum tofu_policy): Define.
(tofu_get_policy): New stub.
(tofu_policy_str): Likewise.
* doc/DETAILS: Describe the TOFU Policy field.
* doc/gpg.texi: Document --tofu-set-policy, --trust-model=tofu,
--trust-model=tofu+pgp, --tofu-default-policy and --tofu-db-format.
* tests/openpgp/Makefile.am (TESTS): Add tofu.test.
(TEST_FILES): Add tofu-keys.asc, tofu-keys-secret.asc,
tofu-2183839A-1.txt, tofu-BC15C85A-1.txt and tofu-EE37CF96-1.txt.
(CLEANFILES): Add tofu.db.
(clean-local): Add tofu.d.
* tests/openpgp/tofu.test: New file.
* tests/openpgp/tofu-2183839A-1.txt: New file.
* tests/openpgp/tofu-BC15C85A-1.txt: New file.
* tests/openpgp/tofu-EE37CF96-1.txt: New file.
* tests/openpgp/tofu-keys.asc: New file.
* tests/openpgp/tofu-keys-secret.asc: New file.

--
Signed-off-by: Neal H. Walfield <neal@g10code.com>.
This commit is contained in:
Neal H. Walfield 2015-10-18 18:44:05 +02:00
parent 93e855553e
commit f77913e0ff
26 changed files with 3508 additions and 80 deletions

View file

@ -26,7 +26,7 @@ AM_CPPFLAGS = -I$(top_srcdir)/common
include $(top_srcdir)/am/cmacros.am
AM_CFLAGS = $(LIBGCRYPT_CFLAGS) \
AM_CFLAGS = $(SQLITE3_CFLAGS) $(LIBGCRYPT_CFLAGS) \
$(LIBASSUAN_CFLAGS) $(GPG_ERROR_CFLAGS)
needed_libs = ../kbx/libkeybox.a $(libcommon)
@ -126,7 +126,8 @@ gpg2_SOURCES = gpg.c \
call-agent.c call-agent.h \
trust.c $(trust_source) \
$(card_source) \
exec.c exec.h
exec.c exec.h \
tofu.h tofu.c
gpgv2_SOURCES = gpgv.c \
$(common_source) \
@ -141,7 +142,7 @@ gpgv2_SOURCES = gpgv.c \
LDADD = $(needed_libs) ../common/libgpgrl.a \
$(ZLIBS) $(LIBINTL) $(CAPLIBS) $(NETLIBS)
gpg2_LDADD = $(LDADD) $(LIBGCRYPT_LIBS) $(LIBREADLINE) \
gpg2_LDADD = $(LDADD) $(SQLITE3_LIBS) $(LIBGCRYPT_LIBS) $(LIBREADLINE) \
$(LIBASSUAN_LIBS) $(GPG_ERROR_LIBS) \
$(LIBICONV) $(resource_objs) $(extra_sys_libs)
gpg2_LDFLAGS = $(extra_bin_ldflags)

140
g10/gpg.c
View file

@ -59,6 +59,7 @@
#include "gc-opt-flags.h"
#include "asshelp.h"
#include "call-dirmngr.h"
#include "tofu.h"
#include "../common/init.h"
#include "../common/shareddefs.h"
@ -162,6 +163,7 @@ enum cmd_and_opt_values
aChangePIN,
aPasswd,
aServer,
aTOFUPolicy,
oTextmode,
oNoTextmode,
@ -385,6 +387,8 @@ enum cmd_and_opt_values
oNoAutostart,
oPrintPKARecords,
oPrintDANERecords,
oTOFUDefaultPolicy,
oTOFUDBFormat,
oNoop
};
@ -475,6 +479,8 @@ static ARGPARSE_OPTS opts[] = {
ARGPARSE_c (aPrimegen, "gen-prime", "@" ),
ARGPARSE_c (aGenRandom,"gen-random", "@" ),
ARGPARSE_c (aServer, "server", N_("run in server mode")),
ARGPARSE_c (aTOFUPolicy, "tofu-policy",
N_("|VALUE|set the TOFU policy for a key (good, unknown, bad, ask, auto)")),
ARGPARSE_group (301, N_("@\nOptions:\n ")),
@ -670,6 +676,8 @@ static ARGPARSE_OPTS opts[] = {
ARGPARSE_s_i (oDefCertLevel, "default-cert-check-level", "@"), /* old */
ARGPARSE_s_n (oAlwaysTrust, "always-trust", "@"),
ARGPARSE_s_s (oTrustModel, "trust-model", "@"),
ARGPARSE_s_s (oTOFUDefaultPolicy, "tofu-default-policy", "@"),
ARGPARSE_s_s (oTOFUDBFormat, "tofu-db-format", "@"),
ARGPARSE_s_s (oSetFilename, "set-filename", "@"),
ARGPARSE_s_n (oForYourEyesOnly, "for-your-eyes-only", "@"),
ARGPARSE_s_n (oNoForYourEyesOnly, "no-for-your-eyes-only", "@"),
@ -1939,6 +1947,10 @@ parse_trust_model(const char *model)
opt.trust_model=TM_ALWAYS;
else if(ascii_strcasecmp(model,"direct")==0)
opt.trust_model=TM_DIRECT;
else if(ascii_strcasecmp(model,"tofu")==0)
opt.trust_model=TM_TOFU;
else if(ascii_strcasecmp(model,"tofu+pgp")==0)
opt.trust_model=TM_TOFU_PGP;
else if(ascii_strcasecmp(model,"auto")==0)
opt.trust_model=TM_AUTO;
else
@ -1946,6 +1958,41 @@ parse_trust_model(const char *model)
}
#endif /*NO_TRUST_MODELS*/
static int
parse_tofu_policy (const char *policy)
{
if (ascii_strcasecmp (policy, "auto") == 0)
return TOFU_POLICY_AUTO;
else if (ascii_strcasecmp (policy, "good") == 0)
return TOFU_POLICY_GOOD;
else if (ascii_strcasecmp (policy, "unknown") == 0)
return TOFU_POLICY_UNKNOWN;
else if (ascii_strcasecmp (policy, "bad") == 0)
return TOFU_POLICY_BAD;
else if (ascii_strcasecmp (policy, "ask") == 0)
return TOFU_POLICY_ASK;
else
{
log_error (_("unknown TOFU policy '%s'\n"), policy);
g10_exit (1);
}
}
static int
parse_tofu_db_format (const char *db_format)
{
if (ascii_strcasecmp (db_format, "auto") == 0)
return TOFU_DB_AUTO;
else if (ascii_strcasecmp (db_format, "split") == 0)
return TOFU_DB_SPLIT;
else if (ascii_strcasecmp (db_format, "flat") == 0)
return TOFU_DB_FLAT;
else
{
log_error (_("unknown TOFU DB format '%s'\n"), db_format);
g10_exit (1);
}
}
/* This fucntion called to initialized a new control object. It is
assumed that this object has been zeroed out before calling this
@ -2150,6 +2197,8 @@ main (int argc, char **argv)
#else
opt.trust_model = TM_AUTO;
#endif
opt.tofu_default_policy = TOFU_POLICY_AUTO;
opt.tofu_db_format = TOFU_DB_AUTO;
opt.mangle_dos_filenames = 0;
opt.min_cert_level = 2;
set_screen_dimensions ();
@ -2372,6 +2421,10 @@ main (int argc, char **argv)
opt.batch = 1;
break;
case aTOFUPolicy:
set_cmd (&cmd, pargs.r_opt);
break;
case oArmor: opt.armor = 1; opt.no_armor=0; break;
case oOutput: opt.outfile = pargs.r.ret_str; break;
case oMaxOutput: opt.max_output = pargs.r.ret_ulong; break;
@ -2553,6 +2606,12 @@ main (int argc, char **argv)
parse_trust_model(pargs.r.ret_str);
break;
#endif /*!NO_TRUST_MODELS*/
case oTOFUDefaultPolicy:
opt.tofu_default_policy = parse_tofu_policy (pargs.r.ret_str);
break;
case oTOFUDBFormat:
opt.tofu_db_format = parse_tofu_db_format (pargs.r.ret_str);
break;
case oForceOwnertrust:
log_info(_("Note: %s is not for normal use!\n"),
@ -4351,6 +4410,87 @@ main (int argc, char **argv)
gcry_control (GCRYCTL_PRINT_CONFIG, stdout);
break;
case aTOFUPolicy:
{
int policy;
int i;
KEYDB_HANDLE hd;
if (argc < 2)
wrong_args("--tofu-policy POLICY KEYID [KEYID...]");
policy = parse_tofu_policy (argv[0]);
hd = keydb_new ();
if (! hd)
{
log_error (_("Failed to open the keyring DB.\n"));
g10_exit (1);
}
for (i = 1; i < argc; i ++)
{
KEYDB_SEARCH_DESC desc;
kbnode_t kb;
rc = classify_user_id (argv[i], &desc, 0);
if (rc)
{
log_error (_("Failed to parse '%s'.\n"), argv[i]);
g10_exit (1);
}
if (! (desc.mode == KEYDB_SEARCH_MODE_SHORT_KID
|| desc.mode == KEYDB_SEARCH_MODE_LONG_KID
|| desc.mode == KEYDB_SEARCH_MODE_FPR16
|| desc.mode == KEYDB_SEARCH_MODE_FPR20
|| desc.mode == KEYDB_SEARCH_MODE_FPR
|| desc.mode == KEYDB_SEARCH_MODE_KEYGRIP))
{
log_error (_("'%s' does not appear to be a valid"
" key id, fingerprint or key grip.\n"),
argv[i]);
g10_exit (1);
}
rc = keydb_search_reset (hd);
if (rc)
{
log_error (_("Failed to reset keyring handle.\n"));
g10_exit (1);
}
rc = keydb_search (hd, &desc, 1, NULL);
if (gpg_err_code (rc) == GPG_ERR_NO_PUBKEY)
{
log_error (_("Key '%s' is not available\n"), argv[i]);
g10_exit (1);
}
else if (rc)
{
log_error (_("Failed to find key '%s'\n"), argv[i]);
g10_exit (1);
}
rc = keydb_get_keyblock (hd, &kb);
if (rc)
{
log_error (_("Failed to read key '%s' from the keyring\n"),
argv[i]);
g10_exit (1);
}
merge_keys_and_selfsig (kb);
if (tofu_set_policy (kb, policy))
g10_exit (1);
}
keydb_release (hd);
}
break;
case aListPackets:
opt.list_packets=2;
default:

View file

@ -285,10 +285,13 @@ get_validity_info (PKT_public_key *pk, PKT_user_id *uid)
}
unsigned int
get_validity (PKT_public_key *pk, PKT_user_id *uid)
get_validity (PKT_public_key *pk, PKT_user_id *uid, PKT_signature *sig,
int may_ask)
{
(void)pk;
(void)uid;
(void)sig;
(void)may_ask;
return 0;
}
@ -606,3 +609,26 @@ export_pubkey_buffer (ctrl_t ctrl, const char *keyspec, unsigned int options,
*r_datalen = 0;
return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
}
enum tofu_policy
{
tofu_policy
};
gpg_error_t
tofu_get_policy (PKT_public_key *pk, PKT_user_id *user_id,
enum tofu_policy *policy)
{
(void)pk;
(void)user_id;
(void)policy;
return gpg_error (GPG_ERR_GENERAL);
}
const char *
tofu_policy_str (enum tofu_policy policy)
{
(void)policy;
return "unknown";
}

View file

@ -47,6 +47,7 @@
#include "keyserver-internal.h"
#include "call-agent.h"
#include "host2net.h"
#include "tofu.h"
static void show_prefs (PKT_user_id * uid, PKT_signature * selfsig,
int verbose);
@ -2927,6 +2928,14 @@ show_key_with_all_names_colon (ctrl_t ctrl, estream_t fp, kbnode_t keyblock)
if ((node->flag & NODFLG_MARK_A))
es_putc ('m', fp);
es_putc (':', fp);
if (opt.trust_model == TM_TOFU || opt.trust_model == TM_TOFU_PGP)
{
enum tofu_policy policy;
if (! tofu_get_policy (primary, uid, &policy)
&& policy != TOFU_POLICY_NONE)
es_fprintf (fp, "%s", tofu_policy_str (policy));
}
es_putc (':', fp);
es_putc ('\n', fp);
}
}
@ -3042,7 +3051,8 @@ show_key_with_all_names (ctrl_t ctrl, estream_t fp,
/* Show a warning once */
if (!did_warn
&& (get_validity (pk, NULL) & TRUST_FLAG_PENDING_CHECK))
&& (get_validity (pk, NULL, NULL, 0)
& TRUST_FLAG_PENDING_CHECK))
{
did_warn = 1;
do_warn = 1;
@ -5334,7 +5344,7 @@ menu_revuid (KBNODE pub_keyblock)
/* If the trustdb has an entry for this key+uid then the
trustdb needs an update. */
if (!update_trust
&& (get_validity (pk, uid) & TRUST_MASK) >=
&& (get_validity (pk, uid, NULL, 0) & TRUST_MASK) >=
TRUST_UNDEFINED)
update_trust = 1;
#endif /*!NO_TRUST_MODELS*/

View file

@ -43,6 +43,7 @@
#include "status.h"
#include "call-agent.h"
#include "mbox-util.h"
#include "tofu.h"
static void list_all (ctrl_t, int, int);
@ -99,7 +100,8 @@ public_key_list (ctrl_t ctrl, strlist_t list, int locate_mode)
es_fprintf (es_stdout, "o");
if (trust_model != opt.trust_model)
es_fprintf (es_stdout, "t");
if (opt.trust_model == TM_PGP || opt.trust_model == TM_CLASSIC)
if (opt.trust_model == TM_PGP || opt.trust_model == TM_CLASSIC
|| opt.trust_model == TM_TOFU_PGP)
{
if (marginals != opt.marginals_needed)
es_fprintf (es_stdout, "m");
@ -1067,7 +1069,7 @@ list_keyblock_print (KBNODE keyblock, int secret, int fpr,
include, but it looks sort of confusing in the listing... */
if (opt.list_options & LIST_SHOW_VALIDITY)
{
int validity = get_validity (pk, NULL);
int validity = get_validity (pk, NULL, NULL, 0);
es_fprintf (es_stdout, " [%s]", trust_value_to_string (validity));
}
#endif
@ -1438,6 +1440,7 @@ list_keyblock_colon (KBNODE keyblock, int secret, int has_secret, int fpr)
xfree (curve);
}
es_putc (':', es_stdout); /* End of field 17. */
es_putc (':', es_stdout); /* End of field 18. */
es_putc ('\n', es_stdout);
print_revokers (es_stdout, pk);
@ -1495,6 +1498,14 @@ list_keyblock_colon (KBNODE keyblock, int secret, int has_secret, int fpr)
es_fprintf (es_stdout, "%u %lu", uid->numattribs, uid->attrib_len);
else
es_write_sanitized (es_stdout, uid->name, uid->len, ":", NULL);
es_fprintf (es_stdout, "::::::::");
if (opt.trust_model == TM_TOFU || opt.trust_model == TM_TOFU_PGP)
{
enum tofu_policy policy;
if (! tofu_get_policy (pk, uid, &policy)
&& policy != TOFU_POLICY_NONE)
es_fprintf (es_stdout, "%s", tofu_policy_str (policy));
}
es_putc (':', es_stdout);
es_putc ('\n', es_stdout);
}

View file

@ -851,6 +851,7 @@ do_check_sig (CTX c, kbnode_t node, int *is_selfsig,
PKT_signature *sig;
gcry_md_hd_t md = NULL;
gcry_md_hd_t md2 = NULL;
gcry_md_hd_t md_good = NULL;
int algo, rc;
assert (node->pkt->pkttype == PKT_SIGNATURE);
@ -926,8 +927,21 @@ do_check_sig (CTX c, kbnode_t node, int *is_selfsig,
return GPG_ERR_SIG_CLASS;
rc = signature_check2 (sig, md, NULL, is_expkey, is_revkey, NULL);
if (gpg_err_code (rc) == GPG_ERR_BAD_SIGNATURE && md2)
rc = signature_check2 (sig, md2, NULL, is_expkey, is_revkey, NULL);
if (! rc)
md_good = md;
else if (gpg_err_code (rc) == GPG_ERR_BAD_SIGNATURE && md2)
{
rc = signature_check2 (sig, md2, NULL, is_expkey, is_revkey, NULL);
if (! rc)
md_good = md2;
}
if (md_good)
{
unsigned char *buffer = gcry_md_read (md_good, 0);
sig->digest_len = gcry_md_get_algo_dlen (map_md_openpgp_to_gcry (algo));
memcpy (sig->digest, buffer, sig->digest_len);
}
gcry_md_close (md);
gcry_md_close (md2);
@ -1848,9 +1862,10 @@ check_sig_and_print (CTX c, kbnode_t node)
assert (pk);
/* Get it before we print anything to avoid interrupting the
output with the "please do a --check-trustdb" line. */
valid = get_validity (pk, un->pkt->pkt.user_id);
/* Since this is just informational, don't actually ask the
user to update any trust information. (Note: we register
the signature later.) */
valid = get_validity (pk, un->pkt->pkt.user_id, NULL, 0);
keyid_str[17] = 0; /* cut off the "[uncertain]" part */
@ -1939,8 +1954,11 @@ check_sig_and_print (CTX c, kbnode_t node)
else if (un->pkt->pkt.user_id->is_expired)
valid = _("expired");
else
/* Since this is just informational, don't
actually ask the user to update any trust
information. */
valid = (trust_value_to_string
(get_validity (pk, un->pkt->pkt.user_id)));
(get_validity (pk, un->pkt->pkt.user_id, sig, 0)));
log_printf (" [%s]\n",valid);
}
else

View file

@ -118,8 +118,16 @@ struct
we started storing the trust model inside the trustdb. */
enum
{
TM_CLASSIC=0, TM_PGP=1, TM_EXTERNAL=2, TM_ALWAYS, TM_DIRECT, TM_AUTO
TM_CLASSIC=0, TM_PGP=1, TM_EXTERNAL=2,
TM_ALWAYS, TM_DIRECT, TM_AUTO, TM_TOFU, TM_TOFU_PGP
} trust_model;
enum
{
TOFU_DB_AUTO=0, TOFU_DB_SPLIT, TOFU_DB_FLAT
} tofu_db_format;
/* TOFU_BINDING_BAD, TOFU_BINDING_ASK, TOFU_BINDING_AUTO, or
TOFU_BINDING_GOOD. */
int tofu_default_policy;
int force_ownertrust;
enum
{

View file

@ -175,6 +175,11 @@ typedef struct
subpktarea_t *unhashed; /* Ditto for unhashed data. */
byte digest_start[2]; /* First 2 bytes of the digest. */
gcry_mpi_t data[PUBKEY_MAX_NSIG];
/* The message digest and its length (in bytes). Note the maximum
digest length is 512 bits (64 bytes). If DIGEST_LEN is 0, then
the digest's value has not been saved here. */
byte digest[512 / 8];
int digest_len;
} PKT_signature;
#define ATTRIB_IMAGE 1

View file

@ -37,6 +37,7 @@
#include "status.h"
#include "photoid.h"
#include "i18n.h"
#include "tofu.h"
#define CONTROL_D ('D' - 'A' + 1)
@ -507,13 +508,13 @@ do_we_trust_pre( PKT_public_key *pk, unsigned int trustlevel )
/****************
* Check whether we can trust this signature.
* Returns: Error if we shall not trust this signatures.
* Returns an error code if we should not trust this signature.
*/
int
check_signatures_trust( PKT_signature *sig )
{
PKT_public_key *pk = xmalloc_clear( sizeof *pk );
unsigned int trustlevel;
unsigned int trustlevel = TRUST_UNKNOWN;
int rc=0;
rc = get_pubkey( pk, sig->keyid );
@ -537,7 +538,7 @@ check_signatures_trust( PKT_signature *sig )
log_info(_("WARNING: this key might be revoked (revocation key"
" not present)\n"));
trustlevel = get_validity (pk, NULL);
trustlevel = get_validity (pk, NULL, sig, 1);
if ( (trustlevel & TRUST_FLAG_REVOKED) )
{
@ -829,7 +830,7 @@ find_and_check_key (ctrl_t ctrl, const char *name, unsigned int use,
}
/* Key found and usable. Check validity. */
trustlevel = get_validity (pk, pk->user_id);
trustlevel = get_validity (pk, pk->user_id, NULL, 1);
if ( (trustlevel & TRUST_FLAG_DISABLED) )
{
/* Key has been disabled. */
@ -1114,7 +1115,7 @@ build_pk_list (ctrl_t ctrl,
{ /* Check validity of this key. */
int trustlevel;
trustlevel = get_validity (pk, pk->user_id);
trustlevel = get_validity (pk, pk->user_id, NULL, 1);
if ( (trustlevel & TRUST_FLAG_DISABLED) )
{
tty_printf (_("Public key is disabled.\n") );

View file

@ -104,10 +104,13 @@ get_validity_info (PKT_public_key *pk, PKT_user_id *uid)
}
unsigned int
get_validity (PKT_public_key *pk, PKT_user_id *uid)
get_validity (PKT_public_key *pk, PKT_user_id *uid, PKT_signature *sig,
int may_ask)
{
(void)pk;
(void)uid;
(void)sig;
(void)may_ask;
return 0;
}
@ -425,3 +428,26 @@ export_pubkey_buffer (ctrl_t ctrl, const char *keyspec, unsigned int options,
*r_datalen = 0;
return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
}
enum tofu_policy
{
tofu_policy
};
gpg_error_t
tofu_get_policy (PKT_public_key *pk, PKT_user_id *user_id,
enum tofu_policy *policy)
{
(void)pk;
(void)user_id;
(void)policy;
return gpg_error (GPG_ERR_GENERAL);
}
const char *
tofu_policy_str (enum tofu_policy policy)
{
(void)policy;
return "unknown";
}

2472
g10/tofu.c Normal file

File diff suppressed because it is too large Load diff

105
g10/tofu.h Normal file
View file

@ -0,0 +1,105 @@
/* tofu.h - TOFU trust model.
* Copyright (C) 2015 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 <http://www.gnu.org/licenses/>.
*/
#ifndef G10_TOFU_H
#define G10_TOFU_H
#include <config.h>
/* For each binding, we have a trust policy. */
enum tofu_policy
{
/* This value can be returned by tofu_get_policy to indicate that
there is no policy set for the specified binding. */
TOFU_POLICY_NONE = 0,
/* We made a default policy decision. This is only done if there
is no conflict with another binding (that is, the email address
is not part of another known key). The default policy is
configurable (and specified using: --tofu-default-policy).
Note: when using the default policy, we save TOFU_POLICY_AUTO
with the binding, not the policy that was in effect. This way,
if the user invokes gpg again, but with a different value for
--tofu-default-policy, a different decision is made. */
TOFU_POLICY_AUTO = 1,
/* The user explicitly marked the binding as good. In this case,
we return TRUST_FULLY. */
TOFU_POLICY_GOOD = 2,
/* The user explicitly marked the binding as unknown. In this
case, we return TRUST_UNKNOWN. */
TOFU_POLICY_UNKNOWN = 3,
/* The user explicitly marked the binding as bad. In this case,
we always return TRUST_NEVER. */
TOFU_POLICY_BAD = 4,
/* The user deferred a definitive policy decision about the
binding (by selecting accept once or reject once). The next
time we see this binding, we should ask the user what to
do. */
TOFU_POLICY_ASK = 5
};
/* Return a string representation of a trust policy. Returns "???" if
POLICY is not valid. */
const char *tofu_policy_str (enum tofu_policy policy);
/* Convert a binding policy (e.g., TOFU_POLICY_BAD) to a trust level
(e.g., TRUST_BAD) in light of the current configuration. */
int tofu_policy_to_trust_level (enum tofu_policy policy);
/* Register the binding <FINGERPRINT, USER_ID> and the signature
described by SIGS_DIGEST and SIG_TIME, which it generated. Origin
describes where the signed data came from, e.g., "email:claws"
(default: "unknown"). If MAY_ASK is 1, then this function may
interact with the user in the case of a conflict or if the
binding's policy is ask. This function returns the binding's trust
level. If an error occurs, it returns TRUST_UNKNOWN. */
int tofu_register (const byte *fingerprint, const char *user_id,
const byte *sigs_digest, int sigs_digest_len,
time_t sig_time, const char *origin, int may_ask);
/* Combine a trust level returned from the TOFU trust model with a
trust level returned by the PGP trust model. This is primarily of
interest when the trust model is tofu+pgp (TM_TOFU_PGP). */
int tofu_wot_trust_combine (int tofu, int wot);
/* Determine the validity (TRUST_NEVER, etc.) of the binding
<FINGERPRINT, USER_ID>. If MAY_ASK is 1, then this function may
interact with the user. If not, TRUST_UNKNOWN is returned. If an
error occurs, TRUST_UNDEFINED is returned. */
int tofu_get_validity (const byte *fingerprint, const char *user_id,
int may_ask);
/* Set the policy for all non-revoked user ids in the keyblock KB to
POLICY. */
gpg_error_t tofu_set_policy (kbnode_t kb, enum tofu_policy policy);
/* Set the TOFU policy for all non-revoked users in the key with the
key id KEYID to POLICY. */
gpg_error_t tofu_set_policy_by_keyid (u32 *keyid, enum tofu_policy policy);
/* Return the TOFU policy for the specified binding in *POLICY. */
gpg_error_t tofu_get_policy (PKT_public_key *pk, PKT_user_id *user_id,
enum tofu_policy *policy);
#endif

View file

@ -152,7 +152,7 @@ uid_trust_string_fixed (PKT_public_key *key, PKT_user_id *uid)
return _("[ expired]");
else if(key)
{
switch (get_validity(key,uid)&TRUST_MASK)
switch (get_validity (key, uid, NULL, 0) & TRUST_MASK)
{
case TRUST_UNKNOWN: return _("[ unknown]");
case TRUST_EXPIRED: return _("[ expired]");
@ -298,7 +298,8 @@ check_or_update_trustdb (void)
* otherwise, a reasonable value for the entire key is returned.
*/
unsigned int
get_validity (PKT_public_key *pk, PKT_user_id *uid)
get_validity (PKT_public_key *pk, PKT_user_id *uid, PKT_signature *sig,
int may_ask)
{
int rc;
unsigned int validity;
@ -330,7 +331,7 @@ get_validity (PKT_public_key *pk, PKT_user_id *uid)
#ifdef NO_TRUST_MODELS
validity = TRUST_UNKNOWN;
#else
validity = tdb_get_validity_core (pk, uid, main_pk);
validity = tdb_get_validity_core (pk, uid, main_pk, sig, may_ask);
#endif
leave:
@ -359,7 +360,7 @@ get_validity_info (PKT_public_key *pk, PKT_user_id *uid)
if (!pk)
return '?'; /* Just in case a NULL PK is passed. */
trustlevel = get_validity (pk, uid);
trustlevel = get_validity (pk, uid, NULL, 0);
if ((trustlevel & TRUST_FLAG_REVOKED))
return 'r';
return trust_letter (trustlevel);
@ -374,7 +375,7 @@ get_validity_string (PKT_public_key *pk, PKT_user_id *uid)
if (!pk)
return "err"; /* Just in case a NULL PK is passed. */
trustlevel = get_validity (pk, uid);
trustlevel = get_validity (pk, uid, NULL, 0);
if ((trustlevel & TRUST_FLAG_REVOKED))
return _("revoked");
return trust_value_to_string (trustlevel);

View file

@ -40,6 +40,7 @@
#include "i18n.h"
#include "tdbio.h"
#include "trustdb.h"
#include "tofu.h"
typedef struct key_item **KeyHashTable; /* see new_key_hash_table() */
@ -379,6 +380,8 @@ trust_model_string(void)
case TM_CLASSIC: return "classic";
case TM_PGP: return "PGP";
case TM_EXTERNAL: return "external";
case TM_TOFU: return "TOFU";
case TM_TOFU_PGP: return "TOFU+PGP";
case TM_ALWAYS: return "always";
case TM_DIRECT: return "direct";
default: return "unknown";
@ -963,16 +966,21 @@ tdb_check_trustdb_stale (void)
/*
* Return the validity information for PK. This is the core of
* get_validity.
* get_validity. If SIG is not NULL, then the trust is being
* evaluated in the context of the provided signature. This is used
* by the TOFU code to record statistics.
*/
unsigned int
tdb_get_validity_core (PKT_public_key *pk, PKT_user_id *uid,
PKT_public_key *main_pk)
PKT_public_key *main_pk,
PKT_signature *sig,
int may_ask)
{
TRUSTREC trec, vrec;
gpg_error_t err;
ulong recno;
unsigned int validity;
unsigned int tofu_validity = TRUST_UNKNOWN;
unsigned int validity = TRUST_UNKNOWN;
init_trustdb ();
@ -993,60 +1001,146 @@ tdb_get_validity_core (PKT_public_key *pk, PKT_user_id *uid,
goto leave;
}
err = read_trust_record (main_pk, &trec);
if (err && gpg_err_code (err) != GPG_ERR_NOT_FOUND)
if (opt.trust_model == TM_TOFU || opt.trust_model == TM_TOFU_PGP)
{
tdbio_invalid ();
return 0;
}
if (gpg_err_code (err) == GPG_ERR_NOT_FOUND)
{
/* No record found. */
validity = TRUST_UNKNOWN;
goto leave;
}
kbnode_t user_id_node;
int user_ids = 0;
int user_ids_expired = 0;
/* Loop over all user IDs */
recno = trec.r.trust.validlist;
validity = 0;
while (recno)
{
read_record (recno, &vrec, RECTYPE_VALID);
char fingerprint[MAX_FINGERPRINT_LEN];
size_t fingerprint_len = sizeof (fingerprint);
if(uid)
fingerprint_from_pk (main_pk, fingerprint, &fingerprint_len);
assert (fingerprint_len == sizeof (fingerprint));
/* If the caller didn't supply a user id then iterate over all
uids. */
if (! uid)
user_id_node = get_pubkeyblock (main_pk->keyid);
while (uid
|| (user_id_node = find_next_kbnode (user_id_node, PKT_USER_ID)))
{
/* If a user ID is given we return the validity for that
user ID ONLY. If the namehash is not found, then there
is no validity at all (i.e. the user ID wasn't
signed). */
if(memcmp(vrec.r.valid.namehash,uid->namehash,20)==0)
unsigned int tl;
PKT_user_id *user_id;
if (uid)
user_id = uid;
else
user_id = user_id_node->pkt->pkt.user_id;
if (user_id->is_revoked || user_id->is_expired)
/* If the user id is revoked or expired, then skip it. */
{
validity=(vrec.r.valid.validity & TRUST_MASK);
break;
char *s;
if (user_id->is_revoked && user_id->is_expired)
s = "revoked and expired";
else if (user_id->is_revoked)
s = "revoked";
else
s = "expire";
log_info ("TOFU: Ignoring %s user id (%s)\n", s, user_id->name);
continue;
}
user_ids ++;
if (sig)
tl = tofu_register (fingerprint, user_id->name,
sig->digest, sig->digest_len,
sig->timestamp, "unknown",
may_ask);
else
tl = tofu_get_validity (fingerprint, user_id->name, may_ask);
if (tl == TRUST_EXPIRED)
user_ids_expired ++;
else if (tl == TRUST_UNDEFINED || tl == TRUST_UNKNOWN)
;
else if (tl == TRUST_NEVER)
tofu_validity = TRUST_NEVER;
else
{
assert (tl == TRUST_MARGINAL
|| tl == TRUST_FULLY
|| tl == TRUST_ULTIMATE);
if (tl > tofu_validity)
/* XXX: We we really want the max? */
tofu_validity = tl;
}
if (uid)
/* If the caller specified a user id, then we stop
now. */
break;
}
}
if (opt.trust_model == TM_TOFU_PGP
|| opt.trust_model == TM_CLASSIC
|| opt.trust_model == TM_PGP)
{
err = read_trust_record (main_pk, &trec);
if (err && gpg_err_code (err) != GPG_ERR_NOT_FOUND)
{
tdbio_invalid ();
return 0;
}
if (gpg_err_code (err) == GPG_ERR_NOT_FOUND)
{
/* No record found. */
validity = TRUST_UNKNOWN;
goto leave;
}
/* Loop over all user IDs */
recno = trec.r.trust.validlist;
validity = 0;
while (recno)
{
read_record (recno, &vrec, RECTYPE_VALID);
if(uid)
{
/* If a user ID is given we return the validity for that
user ID ONLY. If the namehash is not found, then
there is no validity at all (i.e. the user ID wasn't
signed). */
if(memcmp(vrec.r.valid.namehash,uid->namehash,20)==0)
{
validity=(vrec.r.valid.validity & TRUST_MASK);
break;
}
}
else
{
/* If no user ID is given, we take the maximum validity
over all user IDs */
if (validity < (vrec.r.valid.validity & TRUST_MASK))
validity = (vrec.r.valid.validity & TRUST_MASK);
}
recno = vrec.r.valid.next;
}
if ((trec.r.trust.ownertrust & TRUST_FLAG_DISABLED))
{
validity |= TRUST_FLAG_DISABLED;
pk->flags.disabled = 1;
}
else
{
/* If no namehash is given, we take the maximum validity
over all user IDs */
if ( validity < (vrec.r.valid.validity & TRUST_MASK) )
validity = (vrec.r.valid.validity & TRUST_MASK);
}
recno = vrec.r.valid.next;
pk->flags.disabled = 0;
pk->flags.disabled_valid = 1;
}
if ( (trec.r.trust.ownertrust & TRUST_FLAG_DISABLED) )
{
validity |= TRUST_FLAG_DISABLED;
pk->flags.disabled = 1;
}
else
pk->flags.disabled = 0;
pk->flags.disabled_valid = 1;
leave:
if (pending_check_trustdb)
validity = tofu_wot_trust_combine (tofu_validity, validity);
if (opt.trust_model != TM_TOFU
&& pending_check_trustdb)
validity |= TRUST_FLAG_PENDING_CHECK;
return validity;

View file

@ -86,7 +86,8 @@ void revalidation_mark (void);
void check_trustdb_stale (void);
void check_or_update_trustdb (void);
unsigned int get_validity (PKT_public_key *pk, PKT_user_id *uid);
unsigned int get_validity (PKT_public_key *pk, PKT_user_id *uid,
PKT_signature *sig, int may_ask);
int get_validity_info (PKT_public_key *pk, PKT_user_id *uid);
const char *get_validity_string (PKT_public_key *pk, PKT_user_id *uid);
@ -120,7 +121,8 @@ void tdb_check_or_update (void);
int tdb_cache_disabled_value (PKT_public_key *pk);
unsigned int tdb_get_validity_core (PKT_public_key *pk, PKT_user_id *uid,
PKT_public_key *main_pk);
PKT_public_key *main_pk,
PKT_signature *sig, int may_ask);
void list_trust_path( const char *username );
int enum_cert_paths( void **context, ulong *lid,