mirror of
git://git.gnupg.org/gnupg.git
synced 2024-12-22 10:19:57 +01:00
g10: Change tofu_register & tofu_get_validity to process multiple uids.
* g10/tofu.c (tofu_register): Take a list of user ids, not a single user id. Only register the bindings, don't compute the trust. Thus, change return type to an int and remove the may_ask parameter. Update callers. (tofu_get_validity): Take a list of user ids, not a single user id. Update callers. Observe signatures made by expired user ids, but don't include them in the trust calculation. -- Signed-off-by: Neal H. Walfield <neal@g10code.com>
This commit is contained in:
parent
33e97813d7
commit
6052c14709
192
g10/tofu.c
192
g10/tofu.c
@ -2164,8 +2164,9 @@ email_from_user_id (const char *user_id)
|
||||
return email;
|
||||
}
|
||||
|
||||
/* Register the signature with the binding <fingerprint, USER_ID>.
|
||||
The fingerprint is taken from the primary key packet PK.
|
||||
/* Register the signature with the bindings <fingerprint, USER_ID>,
|
||||
for each USER_ID in USER_ID_LIST. The fingerprint is taken from
|
||||
the primary key packet PK.
|
||||
|
||||
SIG_DIGEST_BIN is the binary representation of the message's
|
||||
digest. SIG_DIGEST_BIN_LEN is its length.
|
||||
@ -2181,62 +2182,62 @@ email_from_user_id (const char *user_id)
|
||||
This is necessary if there is a conflict or the binding's policy is
|
||||
TOFU_POLICY_ASK.
|
||||
|
||||
This function returns the binding's trust level on return. If an
|
||||
error occurs, this function returns TRUST_UNKNOWN. */
|
||||
int
|
||||
tofu_register (ctrl_t ctrl, PKT_public_key *pk, const char *user_id,
|
||||
This function returns 0 on success and an error code if an error
|
||||
occured. */
|
||||
gpg_error_t
|
||||
tofu_register (ctrl_t ctrl, PKT_public_key *pk, strlist_t user_id_list,
|
||||
const byte *sig_digest_bin, int sig_digest_bin_len,
|
||||
time_t sig_time, const char *origin, int may_ask)
|
||||
time_t sig_time, const char *origin)
|
||||
{
|
||||
gpg_error_t rc;
|
||||
tofu_dbs_t dbs;
|
||||
char *fingerprint = NULL;
|
||||
strlist_t user_id;
|
||||
char *email = NULL;
|
||||
char *err = NULL;
|
||||
int rc;
|
||||
int trust_level = TRUST_UNKNOWN;
|
||||
char *sig_digest;
|
||||
unsigned long c;
|
||||
int already_verified = 0;
|
||||
|
||||
sig_digest = make_radix64_string (sig_digest_bin, sig_digest_bin_len);
|
||||
|
||||
dbs = opendbs (ctrl);
|
||||
if (! dbs)
|
||||
{
|
||||
rc = gpg_error (GPG_ERR_GENERAL);
|
||||
log_error (_("error opening TOFU database: %s\n"),
|
||||
gpg_strerror (GPG_ERR_GENERAL));
|
||||
goto die;
|
||||
}
|
||||
|
||||
fingerprint = hexfingerprint (pk, NULL, 0);
|
||||
|
||||
if (! *user_id)
|
||||
{
|
||||
log_debug ("TOFU: user id is empty. Can't continue.\n");
|
||||
goto die;
|
||||
}
|
||||
|
||||
email = email_from_user_id (user_id);
|
||||
|
||||
if (! origin)
|
||||
/* The default origin is simply "unknown". */
|
||||
origin = "unknown";
|
||||
|
||||
/* It's necessary to get the trust so that we are certain that the
|
||||
binding has been registered. */
|
||||
trust_level = get_trust (dbs, pk, fingerprint, email, user_id, may_ask);
|
||||
if (trust_level == _tofu_GET_TRUST_ERROR)
|
||||
/* An error. */
|
||||
{
|
||||
trust_level = TRUST_UNKNOWN;
|
||||
goto die;
|
||||
gpg_strerror (rc));
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* We do a query and then an insert. Make sure they are atomic
|
||||
by wrapping them in a transaction. */
|
||||
rc = begin_transaction (ctrl, 0);
|
||||
if (rc)
|
||||
goto die;
|
||||
return rc;
|
||||
|
||||
sig_digest = make_radix64_string (sig_digest_bin, sig_digest_bin_len);
|
||||
fingerprint = hexfingerprint (pk, NULL, 0);
|
||||
|
||||
if (! origin)
|
||||
/* The default origin is simply "unknown". */
|
||||
origin = "unknown";
|
||||
|
||||
for (user_id = user_id_list; user_id; user_id = user_id->next)
|
||||
{
|
||||
email = email_from_user_id (user_id->d);
|
||||
|
||||
if (DBG_TRUST)
|
||||
log_debug ("TOFU: Registering signature %s with binding"
|
||||
" <key: %s, user id: %s>\n",
|
||||
sig_digest, fingerprint, email);
|
||||
|
||||
/* Make sure the binding exists and record any TOFU
|
||||
conflicts. */
|
||||
if (get_trust (dbs, pk, fingerprint, email, user_id->d, 0)
|
||||
== _tofu_GET_TRUST_ERROR)
|
||||
{
|
||||
rc = gpg_error (GPG_ERR_GENERAL);
|
||||
xfree (email);
|
||||
break;
|
||||
}
|
||||
|
||||
/* If we've already seen this signature before, then don't add
|
||||
it again. */
|
||||
@ -2263,16 +2264,17 @@ tofu_register (ctrl_t ctrl, PKT_public_key *pk, const char *user_id,
|
||||
because <fingerprint, email, sig_time, sig_digest> is the
|
||||
primary key! */
|
||||
log_debug ("SIGNATURES DB contains duplicate records"
|
||||
" <%s, %s, 0x%lx, %s, %s>."
|
||||
" <key: %s, fingerprint: %s, time: 0x%lx, sig: %s,"
|
||||
" origin: %s>."
|
||||
" Please report.\n",
|
||||
fingerprint, email, (unsigned long) sig_time,
|
||||
sig_digest, origin);
|
||||
else if (c == 1)
|
||||
{
|
||||
already_verified = 1;
|
||||
if (DBG_TRUST)
|
||||
log_debug ("Already observed the signature"
|
||||
" <%s, %s, 0x%lx, %s, %s>\n",
|
||||
log_debug ("Already observed the signature and binding"
|
||||
" <key: %s, user id: %s, time: 0x%lx, sig: %s,"
|
||||
" origin: %s>\n",
|
||||
fingerprint, email, (unsigned long) sig_time,
|
||||
sig_digest, origin);
|
||||
}
|
||||
@ -2281,11 +2283,12 @@ tofu_register (ctrl_t ctrl, PKT_public_key *pk, const char *user_id,
|
||||
log_info ("TOFU database update skipped due to --dry-run\n");
|
||||
}
|
||||
else
|
||||
/* This is the first time that we've seen this signature.
|
||||
Record it. */
|
||||
/* This is the first time that we've seen this signature and
|
||||
binding. Record it. */
|
||||
{
|
||||
if (DBG_TRUST)
|
||||
log_debug ("TOFU: Saving signature <%s, %s, %s>\n",
|
||||
log_debug ("TOFU: Saving signature"
|
||||
" <key: %s, user id: %s, sig: %s>\n",
|
||||
fingerprint, email, sig_digest);
|
||||
|
||||
log_assert (c == 0);
|
||||
@ -2310,30 +2313,21 @@ tofu_register (ctrl_t ctrl, PKT_public_key *pk, const char *user_id,
|
||||
}
|
||||
}
|
||||
|
||||
/* It only matters whether we abort or commit the transaction
|
||||
(so long as we do something) if we execute the insert. */
|
||||
xfree (email);
|
||||
|
||||
if (rc)
|
||||
rc = rollback_transaction (ctrl);
|
||||
else
|
||||
rc = end_transaction (ctrl, 0);
|
||||
if (rc)
|
||||
{
|
||||
sqlite3_free (err);
|
||||
goto die;
|
||||
break;
|
||||
}
|
||||
|
||||
die:
|
||||
if (may_ask && trust_level != TRUST_ULTIMATE)
|
||||
/* It's only appropriate to show the statistics in an interactive
|
||||
context. */
|
||||
show_statistics (dbs, fingerprint, email, user_id,
|
||||
already_verified ? NULL : sig_digest, NULL);
|
||||
if (rc)
|
||||
rollback_transaction (ctrl);
|
||||
else
|
||||
rc = end_transaction (ctrl, 0);
|
||||
|
||||
xfree (email);
|
||||
xfree (fingerprint);
|
||||
xfree (sig_digest);
|
||||
|
||||
return trust_level;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Combine a trust level returned from the TOFU trust model with a
|
||||
@ -2431,8 +2425,9 @@ tofu_write_tfs_record (ctrl_t ctrl, estream_t fp,
|
||||
}
|
||||
|
||||
|
||||
/* Return the validity (TRUST_NEVER, etc.) of the binding
|
||||
<FINGERPRINT, USER_ID>.
|
||||
/* Return the validity (TRUST_NEVER, etc.) of the bindings
|
||||
<FINGERPRINT, USER_ID>, for each USER_ID in USER_ID_LIST. If
|
||||
USER_ID_LIST->FLAG is set, then the id is considered to be expired.
|
||||
|
||||
PK is the primary key packet.
|
||||
|
||||
@ -2442,43 +2437,80 @@ tofu_write_tfs_record (ctrl_t ctrl, estream_t fp,
|
||||
|
||||
Returns TRUST_UNDEFINED if an error occurs. */
|
||||
int
|
||||
tofu_get_validity (ctrl_t ctrl, PKT_public_key *pk, const char *user_id,
|
||||
tofu_get_validity (ctrl_t ctrl, PKT_public_key *pk, strlist_t user_id_list,
|
||||
int may_ask)
|
||||
{
|
||||
tofu_dbs_t dbs;
|
||||
char *fingerprint = NULL;
|
||||
char *email = NULL;
|
||||
int trust_level = TRUST_UNDEFINED;
|
||||
strlist_t user_id;
|
||||
int trust_level = TRUST_UNKNOWN;
|
||||
|
||||
dbs = opendbs (ctrl);
|
||||
if (! dbs)
|
||||
{
|
||||
log_error (_("error opening TOFU database: %s\n"),
|
||||
gpg_strerror (GPG_ERR_GENERAL));
|
||||
goto die;
|
||||
return TRUST_UNDEFINED;
|
||||
}
|
||||
|
||||
fingerprint = hexfingerprint (pk, NULL, 0);
|
||||
|
||||
if (! *user_id)
|
||||
begin_transaction (ctrl, 0);
|
||||
|
||||
for (user_id = user_id_list; user_id; user_id = user_id->next)
|
||||
{
|
||||
log_debug ("user id is empty."
|
||||
" Can't get TOFU validity for this binding.\n");
|
||||
char *email = email_from_user_id (user_id->d);
|
||||
|
||||
/* Always call get_trust to make sure the binding is
|
||||
registered. */
|
||||
int tl = get_trust (dbs, pk, fingerprint, email, user_id->d, may_ask);
|
||||
if (tl == _tofu_GET_TRUST_ERROR)
|
||||
{
|
||||
/* An error. */
|
||||
trust_level = TRUST_UNDEFINED;
|
||||
xfree (email);
|
||||
goto die;
|
||||
}
|
||||
|
||||
email = email_from_user_id (user_id);
|
||||
if (DBG_TRUST)
|
||||
log_debug ("TOFU: validity for <key: %s, user id: %s>: %s%s.\n",
|
||||
fingerprint, email,
|
||||
trust_value_to_string (tl),
|
||||
user_id->flags ? " (but expired)" : "");
|
||||
|
||||
trust_level = get_trust (dbs, pk, fingerprint, email, user_id, may_ask);
|
||||
if (trust_level == _tofu_GET_TRUST_ERROR)
|
||||
/* An error. */
|
||||
trust_level = TRUST_UNDEFINED;
|
||||
if (user_id->flags)
|
||||
tl = TRUST_EXPIRED;
|
||||
|
||||
if (may_ask && trust_level != TRUST_ULTIMATE)
|
||||
show_statistics (dbs, fingerprint, email, user_id, NULL, NULL);
|
||||
if (may_ask && tl != TRUST_ULTIMATE && tl != TRUST_EXPIRED)
|
||||
show_statistics (dbs, fingerprint, email, user_id->d, NULL, NULL);
|
||||
|
||||
if (tl == TRUST_NEVER)
|
||||
trust_level = TRUST_NEVER;
|
||||
else if (tl == TRUST_EXPIRED)
|
||||
/* Ignore expired bindings in the trust calculation. */
|
||||
;
|
||||
else if (tl > trust_level)
|
||||
{
|
||||
/* The expected values: */
|
||||
log_assert (tl == TRUST_UNKNOWN || tl == TRUST_UNDEFINED
|
||||
|| tl == TRUST_MARGINAL || tl == TRUST_FULLY
|
||||
|| tl == TRUST_ULTIMATE);
|
||||
|
||||
/* We assume the following ordering: */
|
||||
log_assert (TRUST_UNKNOWN < TRUST_UNDEFINED);
|
||||
log_assert (TRUST_UNDEFINED < TRUST_MARGINAL);
|
||||
log_assert (TRUST_MARGINAL < TRUST_FULLY);
|
||||
log_assert (TRUST_FULLY < TRUST_ULTIMATE);
|
||||
|
||||
trust_level = tl;
|
||||
}
|
||||
|
||||
xfree (email);
|
||||
}
|
||||
|
||||
die:
|
||||
xfree (email);
|
||||
end_transaction (ctrl, 0);
|
||||
|
||||
xfree (fingerprint);
|
||||
return trust_level;
|
||||
}
|
||||
|
36
g10/tofu.h
36
g10/tofu.h
@ -59,7 +59,7 @@ enum tofu_policy
|
||||
TOFU_POLICY_ASK = 5,
|
||||
|
||||
|
||||
/* Privat evalue used only within tofu.c. */
|
||||
/* Private value used only within tofu.c. */
|
||||
_tofu_GET_POLICY_ERROR = 100
|
||||
};
|
||||
|
||||
@ -72,16 +72,19 @@ const char *tofu_policy_str (enum tofu_policy policy);
|
||||
(e.g., TRUST_BAD) in light of the current configuration. */
|
||||
int tofu_policy_to_trust_level (enum tofu_policy policy);
|
||||
|
||||
/* Register the binding <PK, 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 (ctrl_t ctrl, PKT_public_key *pk, const char *user_id,
|
||||
/* Register the bindings <PK, USER_ID>, for each USER_ID in
|
||||
USER_ID_LIST, 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"). Note:
|
||||
this function does not interact with the user, If there is a
|
||||
conflict, or if the binding's policy is ask, the actual interaction
|
||||
is deferred until tofu_get_validity is called.. Set the string
|
||||
list FLAG to indicate that a specified user id is expired. This
|
||||
function returns 0 on success and an error code on failure. */
|
||||
gpg_error_t tofu_register (ctrl_t ctrl, PKT_public_key *pk,
|
||||
strlist_t user_id_list,
|
||||
const byte *sigs_digest, int sigs_digest_len,
|
||||
time_t sig_time, const char *origin, int may_ask);
|
||||
time_t sig_time, const char *origin);
|
||||
|
||||
/* Combine a trust level returned from the TOFU trust model with a
|
||||
trust level returned by the PGP trust model. This is primarily of
|
||||
@ -92,12 +95,15 @@ int tofu_wot_trust_combine (int tofu, int wot);
|
||||
gpg_error_t tofu_write_tfs_record (ctrl_t ctrl, estream_t fp,
|
||||
PKT_public_key *pk, const char *user_id);
|
||||
|
||||
/* Determine the validity (TRUST_NEVER, etc.) of the binding
|
||||
<PK, 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. */
|
||||
/* Determine the validity (TRUST_NEVER, etc.) of the binding <PK,
|
||||
USER_ID>. If MAY_ASK is 1, then this function may interact with
|
||||
the user. If not, TRUST_UNKNOWN is returned if an interaction is
|
||||
required. Set the string list FLAGS to indicate that a specified
|
||||
user id is expired. If an error occurs, TRUST_UNDEFINED is
|
||||
returned. */
|
||||
int tofu_get_validity (ctrl_t ctrl,
|
||||
PKT_public_key *pk, const char *user_id, int may_ask);
|
||||
PKT_public_key *pk, strlist_t user_id_list,
|
||||
int may_ask);
|
||||
|
||||
/* Set the policy for all non-revoked user ids in the keyblock KB to
|
||||
POLICY. */
|
||||
|
@ -988,7 +988,7 @@ tdb_get_validity_core (ctrl_t ctrl,
|
||||
int may_ask)
|
||||
{
|
||||
TRUSTREC trec, vrec;
|
||||
gpg_error_t err;
|
||||
gpg_error_t err = 0;
|
||||
ulong recno;
|
||||
#ifdef USE_TOFU
|
||||
unsigned int tofu_validity = TRUST_UNKNOWN;
|
||||
@ -1022,21 +1022,18 @@ tdb_get_validity_core (ctrl_t ctrl,
|
||||
#ifdef USE_TOFU
|
||||
if (opt.trust_model == TM_TOFU || opt.trust_model == TM_TOFU_PGP)
|
||||
{
|
||||
kbnode_t user_id_node = NULL;
|
||||
kbnode_t n = NULL; /* Silence -Wmaybe-uninitialized. */
|
||||
int user_ids = 0;
|
||||
int user_ids_expired = 0;
|
||||
kbnode_t kb = NULL;
|
||||
kbnode_t n = NULL;
|
||||
strlist_t user_id_list = NULL;
|
||||
|
||||
/* If the caller didn't supply a user id then iterate over all
|
||||
uids. */
|
||||
/* If the caller didn't supply a user id then use all uids. */
|
||||
if (! uid)
|
||||
user_id_node = n = get_pubkeyblock (main_pk->keyid);
|
||||
kb = n = get_pubkeyblock (main_pk->keyid);
|
||||
|
||||
while (uid
|
||||
|| (n = find_next_kbnode (n, PKT_USER_ID)))
|
||||
while (uid || (n = find_next_kbnode (n, PKT_USER_ID)))
|
||||
{
|
||||
unsigned int tl;
|
||||
PKT_user_id *user_id;
|
||||
int expired = 0;
|
||||
|
||||
if (uid)
|
||||
user_id = uid;
|
||||
@ -1060,42 +1057,48 @@ tdb_get_validity_core (ctrl_t ctrl,
|
||||
s, user_id->name);
|
||||
}
|
||||
|
||||
if (user_id->is_revoked)
|
||||
continue;
|
||||
|
||||
expired = 1;
|
||||
}
|
||||
|
||||
user_ids ++;
|
||||
|
||||
if (sig)
|
||||
tl = tofu_register (ctrl, main_pk, user_id->name,
|
||||
sig->digest, sig->digest_len,
|
||||
sig->timestamp, "unknown",
|
||||
may_ask);
|
||||
else
|
||||
tl = tofu_get_validity (ctrl, main_pk, 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
|
||||
{
|
||||
log_assert (tl == TRUST_MARGINAL
|
||||
|| tl == TRUST_FULLY
|
||||
|| tl == TRUST_ULTIMATE);
|
||||
|
||||
if (tl > tofu_validity)
|
||||
/* XXX: We we really want the max? */
|
||||
tofu_validity = tl;
|
||||
}
|
||||
add_to_strlist (&user_id_list, user_id->name);
|
||||
user_id_list->flags = expired;
|
||||
|
||||
if (uid)
|
||||
/* If the caller specified a user id, then we stop
|
||||
now. */
|
||||
break;
|
||||
}
|
||||
release_kbnode (user_id_node);
|
||||
|
||||
/* Process the user ids in the order they appear in the key
|
||||
block. */
|
||||
strlist_rev (&user_id_list);
|
||||
|
||||
/* It only makes sense to observe any signature before getting
|
||||
the validity. This is because if the current signature
|
||||
results in a conflict, then we damn well want to take that
|
||||
into account. */
|
||||
if (sig)
|
||||
{
|
||||
err = tofu_register (ctrl, main_pk, user_id_list,
|
||||
sig->digest, sig->digest_len,
|
||||
sig->timestamp, "unknown");
|
||||
if (err)
|
||||
{
|
||||
log_error ("TOFU: error registering signature: %s\n",
|
||||
gpg_strerror (err));
|
||||
|
||||
tofu_validity = TRUST_UNKNOWN;
|
||||
}
|
||||
}
|
||||
if (! err)
|
||||
tofu_validity = tofu_get_validity (ctrl, main_pk, user_id_list,
|
||||
may_ask);
|
||||
|
||||
free_strlist (user_id_list);
|
||||
release_kbnode (kb);
|
||||
}
|
||||
#endif /*USE_TOFU*/
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user