1
0
mirror of git://git.gnupg.org/gnupg.git synced 2025-04-17 15:44:34 +02:00

g10: Be careful to not be in a transaction during long operations

* g10/tofu.c (begin_transaction): New parameter only_batch.  If set,
only start a batch transaction if there is none and one has been
requested.  Update callers.
(tofu_suspend_batch_transaction): New function.
(tofu_resume_batch_transaction): Likewise.
(ask_about_binding): Take a ctrl_t, not a tofu_dbs_t.  Update
callers.  Gather statistics within a transaction.  Suspend any batch
transaction when getting user input.
(get_trust): Take a ctrl_t, not a tofu_dbs_t.  Update callers.
Enclose in a transaction.
(tofu_get_validity): Use a batch transaction, not a normal
transaction.

--
Signed-off-by: Neal H. Walfield <neal@g10code.com>
This commit is contained in:
Neal H. Walfield 2016-09-01 12:42:44 +02:00
parent babeb6f8a9
commit 4cbd2a690c

View File

@ -162,9 +162,11 @@ tofu_policy_to_trust_level (enum tofu_policy policy)
/* Start a transaction on DB. */ /* Start a transaction on DB. If ONLY_BATCH is set, then this will
start a batch transaction if we haven't started a batch transaction
and one has been requested. */
static gpg_error_t static gpg_error_t
begin_transaction (ctrl_t ctrl) begin_transaction (ctrl_t ctrl, int only_batch)
{ {
tofu_dbs_t dbs = ctrl->tofu.dbs; tofu_dbs_t dbs = ctrl->tofu.dbs;
int rc; int rc;
@ -220,6 +222,9 @@ begin_transaction (ctrl_t ctrl)
dbs->batch_update_started = gnupg_get_time (); dbs->batch_update_started = gnupg_get_time ();
} }
if (only_batch)
return 0;
log_assert(dbs->in_transaction >= 0); log_assert(dbs->in_transaction >= 0);
dbs->in_transaction ++; dbs->in_transaction ++;
@ -289,6 +294,9 @@ end_transaction (ctrl_t ctrl, int only_batch)
rc = gpgsql_exec_printf (dbs->db, NULL, NULL, &err, rc = gpgsql_exec_printf (dbs->db, NULL, NULL, &err,
"release inner%d;", dbs->in_transaction); "release inner%d;", dbs->in_transaction);
dbs->in_transaction --;
if (rc) if (rc)
{ {
log_error (_("error committing transaction on TOFU database: %s\n"), log_error (_("error committing transaction on TOFU database: %s\n"),
@ -297,8 +305,6 @@ end_transaction (ctrl_t ctrl, int only_batch)
return gpg_error (GPG_ERR_GENERAL); return gpg_error (GPG_ERR_GENERAL);
} }
dbs->in_transaction --;
return 0; return 0;
} }
@ -343,11 +349,28 @@ tofu_end_batch_update (ctrl_t ctrl)
{ {
log_assert (ctrl->tofu.batch_updated_wanted > 0); log_assert (ctrl->tofu.batch_updated_wanted > 0);
ctrl->tofu.batch_updated_wanted --; ctrl->tofu.batch_updated_wanted --;
end_transaction (ctrl, 1);
if (!ctrl->tofu.batch_updated_wanted)
end_transaction (ctrl, 1);
} }
/* Suspend any extant batch transaction (it is safe to call this even
no batch transaction has been started). Note: you cannot suspend a
batch transaction if you are in a normal transaction. The batch
transaction can be resumed explicitly by calling
tofu_resume_batch_transaction or implicitly by starting a normal
transaction. */
static void
tofu_suspend_batch_transaction (ctrl_t ctrl)
{
end_transaction (ctrl, 2);
}
/* Resume a batch transaction if there is no extant batch transaction
and one has been requested using tofu_begin_batch_transaction. */
static void
tofu_resume_batch_transaction (ctrl_t ctrl)
{
begin_transaction (ctrl, 1);
}
@ -1193,9 +1216,11 @@ format_conflict_msg_part1 (int policy, const char *conflict,
* *
* - The policy is ask (the user deferred last time) (policy == * - The policy is ask (the user deferred last time) (policy ==
* TOFU_POLICY_ASK). * TOFU_POLICY_ASK).
*
* Note: this function must not be called while in a transaction!
*/ */
static void static void
ask_about_binding (tofu_dbs_t dbs, ask_about_binding (ctrl_t ctrl,
enum tofu_policy *policy, enum tofu_policy *policy,
int *trust_level, int *trust_level,
int bindings_with_this_email_count, int bindings_with_this_email_count,
@ -1205,6 +1230,7 @@ ask_about_binding (tofu_dbs_t dbs,
const char *email, const char *email,
const char *user_id) const char *user_id)
{ {
tofu_dbs_t dbs;
char *sqerr = NULL; char *sqerr = NULL;
int rc; int rc;
estream_t fp; estream_t fp;
@ -1214,6 +1240,10 @@ ask_about_binding (tofu_dbs_t dbs,
char *prompt; char *prompt;
char *choices; char *choices;
dbs = ctrl->tofu.dbs;
log_assert (dbs);
log_assert (dbs->in_transaction == 0);
fp = es_fopenmem (0, "rw,samethread"); fp = es_fopenmem (0, "rw,samethread");
if (!fp) if (!fp)
log_fatal ("error creating memory stream: %s\n", log_fatal ("error creating memory stream: %s\n",
@ -1227,6 +1257,8 @@ ask_about_binding (tofu_dbs_t dbs,
xfree (text); xfree (text);
} }
begin_transaction (ctrl, 0);
/* Find other user ids associated with this key and whether the /* Find other user ids associated with this key and whether the
* bindings are marked as good or bad. */ * bindings are marked as good or bad. */
rc = gpgsql_stepx rc = gpgsql_stepx
@ -1495,6 +1527,7 @@ ask_about_binding (tofu_dbs_t dbs,
} }
} }
end_transaction (ctrl, 0);
if ((*policy == TOFU_POLICY_NONE && bindings_with_this_email_count > 0) if ((*policy == TOFU_POLICY_NONE && bindings_with_this_email_count > 0)
|| (*policy == TOFU_POLICY_ASK || (*policy == TOFU_POLICY_ASK
@ -1536,6 +1569,10 @@ ask_about_binding (tofu_dbs_t dbs,
* wrong choise (because he does not see that either). As a small * wrong choise (because he does not see that either). As a small
* benefit we allow C-L to redisplay everything. */ * benefit we allow C-L to redisplay everything. */
tty_printf ("%s", prompt); tty_printf ("%s", prompt);
/* Suspend any transaction: it could take a while until the user
responds. */
tofu_suspend_batch_transaction (ctrl);
while (1) while (1)
{ {
char *response; char *response;
@ -1599,6 +1636,7 @@ ask_about_binding (tofu_dbs_t dbs,
} }
xfree (response); xfree (response);
} }
tofu_resume_batch_transaction (ctrl);
xfree (prompt); xfree (prompt);
@ -1619,12 +1657,15 @@ ask_about_binding (tofu_dbs_t dbs,
* necessary if there is a conflict or the binding's policy is * necessary if there is a conflict or the binding's policy is
* TOFU_POLICY_ASK. In the case of a conflict, we set the new * TOFU_POLICY_ASK. In the case of a conflict, we set the new
* conflicting binding's policy to TOFU_POLICY_ASK. In either case, * conflicting binding's policy to TOFU_POLICY_ASK. In either case,
* we return TRUST_UNDEFINED. */ * we return TRUST_UNDEFINED. Note: if MAY_ASK is set, then this
* function must not be called while in a transaction! */
static enum tofu_policy static enum tofu_policy
get_trust (tofu_dbs_t dbs, PKT_public_key *pk, get_trust (ctrl_t ctrl, PKT_public_key *pk,
const char *fingerprint, const char *email, const char *fingerprint, const char *email,
const char *user_id, int may_ask) const char *user_id, int may_ask)
{ {
tofu_dbs_t dbs = ctrl->tofu.dbs;
int in_transaction = 0;
enum tofu_policy policy; enum tofu_policy policy;
char *conflict = NULL; char *conflict = NULL;
int rc; int rc;
@ -1634,6 +1675,11 @@ get_trust (tofu_dbs_t dbs, PKT_public_key *pk,
int change_conflicting_to_ask = 0; int change_conflicting_to_ask = 0;
int trust_level = TRUST_UNKNOWN; int trust_level = TRUST_UNKNOWN;
log_assert (dbs);
if (may_ask)
log_assert (dbs->in_transaction == 0);
if (opt.batch) if (opt.batch)
may_ask = 0; may_ask = 0;
@ -1647,6 +1693,9 @@ get_trust (tofu_dbs_t dbs, PKT_public_key *pk,
&& _tofu_GET_TRUST_ERROR != TRUST_FULLY && _tofu_GET_TRUST_ERROR != TRUST_FULLY
&& _tofu_GET_TRUST_ERROR != TRUST_ULTIMATE); && _tofu_GET_TRUST_ERROR != TRUST_ULTIMATE);
begin_transaction (ctrl, 0);
in_transaction = 1;
policy = get_policy (dbs, fingerprint, email, &conflict); policy = get_policy (dbs, fingerprint, email, &conflict);
{ {
/* See if the key is ultimately trusted. If so, we're done. */ /* See if the key is ultimately trusted. If so, we're done. */
@ -1813,8 +1862,12 @@ get_trust (tofu_dbs_t dbs, PKT_public_key *pk,
goto out; goto out;
} }
/* We can't be in a normal transaction in ask_about_binding. */
end_transaction (ctrl, 0);
in_transaction = 0;
/* If we get here, we need to ask the user about the binding. */ /* If we get here, we need to ask the user about the binding. */
ask_about_binding (dbs, ask_about_binding (ctrl,
&policy, &policy,
&trust_level, &trust_level,
bindings_with_this_email_count, bindings_with_this_email_count,
@ -1825,6 +1878,9 @@ get_trust (tofu_dbs_t dbs, PKT_public_key *pk,
user_id); user_id);
out: out:
if (in_transaction)
end_transaction (ctrl, 0);
if (change_conflicting_to_ask) if (change_conflicting_to_ask)
{ {
if (! may_ask) if (! may_ask)
@ -2334,7 +2390,7 @@ tofu_register (ctrl_t ctrl, PKT_public_key *pk, strlist_t user_id_list,
/* We do a query and then an insert. Make sure they are atomic /* We do a query and then an insert. Make sure they are atomic
by wrapping them in a transaction. */ by wrapping them in a transaction. */
rc = begin_transaction (ctrl); rc = begin_transaction (ctrl, 0);
if (rc) if (rc)
return rc; return rc;
@ -2356,7 +2412,7 @@ tofu_register (ctrl_t ctrl, PKT_public_key *pk, strlist_t user_id_list,
/* Make sure the binding exists and record any TOFU /* Make sure the binding exists and record any TOFU
conflicts. */ conflicts. */
if (get_trust (dbs, pk, fingerprint, email, user_id->d, 0) if (get_trust (ctrl, pk, fingerprint, email, user_id->d, 0)
== _tofu_GET_TRUST_ERROR) == _tofu_GET_TRUST_ERROR)
{ {
rc = gpg_error (GPG_ERR_GENERAL); rc = gpg_error (GPG_ERR_GENERAL);
@ -2557,8 +2613,8 @@ tofu_write_tfs_record (ctrl_t ctrl, estream_t fp,
PK is the primary key packet. PK is the primary key packet.
If MAY_ASK is 1 and the policy is TOFU_POLICY_ASK, then the user If MAY_ASK is 1 and the policy is TOFU_POLICY_ASK, then the user
will be prompted to choose a different policy. If MAY_ASK is 0 and will be prompted to choose a policy. If MAY_ASK is 0 and the
the policy is TOFU_POLICY_ASK, then TRUST_UNKNOWN is returned. policy is TOFU_POLICY_ASK, then TRUST_UNKNOWN is returned.
Returns TRUST_UNDEFINED if an error occurs. */ Returns TRUST_UNDEFINED if an error occurs. */
int int
@ -2582,7 +2638,8 @@ tofu_get_validity (ctrl_t ctrl, PKT_public_key *pk, strlist_t user_id_list,
fingerprint = hexfingerprint (pk, NULL, 0); fingerprint = hexfingerprint (pk, NULL, 0);
begin_transaction (ctrl); tofu_begin_batch_update (ctrl);
tofu_resume_batch_transaction (ctrl);
for (user_id = user_id_list; user_id; user_id = user_id->next, bindings ++) for (user_id = user_id_list; user_id; user_id = user_id->next, bindings ++)
{ {
@ -2590,7 +2647,7 @@ tofu_get_validity (ctrl_t ctrl, PKT_public_key *pk, strlist_t user_id_list,
/* Always call get_trust to make sure the binding is /* Always call get_trust to make sure the binding is
registered. */ registered. */
int tl = get_trust (dbs, pk, fingerprint, email, user_id->d, may_ask); int tl = get_trust (ctrl, pk, fingerprint, email, user_id->d, may_ask);
if (tl == _tofu_GET_TRUST_ERROR) if (tl == _tofu_GET_TRUST_ERROR)
{ {
/* An error. */ /* An error. */
@ -2639,7 +2696,7 @@ tofu_get_validity (ctrl_t ctrl, PKT_public_key *pk, strlist_t user_id_list,
} }
die: die:
end_transaction (ctrl, 0); tofu_end_batch_update (ctrl);
xfree (fingerprint); xfree (fingerprint);
@ -2689,7 +2746,7 @@ tofu_set_policy (ctrl_t ctrl, kbnode_t kb, enum tofu_policy policy)
fingerprint = hexfingerprint (pk, NULL, 0); fingerprint = hexfingerprint (pk, NULL, 0);
begin_transaction (ctrl); begin_transaction (ctrl, 0);
for (; kb; kb = kb->next) for (; kb; kb = kb->next)
{ {