bus/policy: separate prefix rules in default context

To handle prefix rules stored with all other rules in the default context
we need to match each prefix of each name against policy rules.
That's because names are looked up in the hash tables, so we can
miss a prefix rule for a prefix of the name.

However, if prefix rules are separated from non-prefix rules, we
can simply check them all once for each name, and also check hash tables once
for each name.

This is what this commit changes. It separates prefix rules
from non-prefix rules, and handles them in sequence.

This gives a little boost, especially if there are no prefix rules.

Change-Id: Ifade906d35af96a973920ce9c2f6065f5b9b549e
This commit is contained in:
Adrian Szyndela 2022-04-27 14:10:28 +02:00
parent dc74021f5a
commit 626cb5cf4e

View file

@ -152,6 +152,7 @@ struct BusPolicy
DBusList *at_console_false_rules; /**< console user policy rules where at_console="false"*/
DBusHashTable *default_rules_by_name;
DBusList *default_prefix_rules;
unsigned int n_default_rules;
};
@ -285,6 +286,9 @@ bus_policy_unref (BusPolicy *policy)
policy->default_rules_by_name = NULL;
}
_dbus_list_foreach (&policy->default_prefix_rules, free_rule_func, NULL);
_dbus_list_clear (&policy->default_prefix_rules);
dbus_free (policy);
}
}
@ -484,6 +488,17 @@ get_name_from_rule (BusPolicyRule *rule)
return name;
}
static dbus_bool_t
is_prefix_rule (BusPolicyRule *rule)
{
if (rule->type == BUS_POLICY_RULE_SEND && rule->d.send.destination_is_prefix)
return TRUE;
if (rule->type == BUS_POLICY_RULE_OWN && rule->d.own.prefix)
return TRUE;
return FALSE;
}
dbus_bool_t
bus_policy_append_default_rule (BusPolicy *policy,
BusPolicyRule *rule)
@ -495,22 +510,31 @@ bus_policy_append_default_rule (BusPolicy *policy,
}
else
{
DBusList **list;
BusPolicyRulesWithScore *rules;
rules = get_rules_by_string (policy->default_rules_by_name,
get_name_from_rule (rule));
if (rules == NULL)
return FALSE;
list = &rules->rules;
if (!_dbus_list_prepend (list, rule))
return FALSE;
rule->score = ++policy->n_default_rules;
rules->score = rule->score;
if (is_prefix_rule (rule))
{
if (!_dbus_list_append (&policy->default_prefix_rules, rule))
return FALSE;
}
else
{
DBusList **list;
BusPolicyRulesWithScore *rules;
rules = get_rules_by_string (policy->default_rules_by_name,
get_name_from_rule (rule));
if (rules == NULL)
return FALSE;
list = &rules->rules;
if (!_dbus_list_prepend (list, rule))
return FALSE;
rules->score = rule->score;
}
}
bus_policy_rule_ref (rule);
@ -769,6 +793,10 @@ bus_policy_merge (BusPolicy *policy,
to_absorb->default_rules_by_name))
return FALSE;
if (!append_copy_of_policy_list (&policy->default_prefix_rules,
&to_absorb->default_prefix_rules))
return FALSE;
return TRUE;
}
@ -1321,33 +1349,27 @@ check_rules_list (const DBusList *rules,
}
static int
check_rules_for_name (DBusHashTable *rules,
const char *name,
int score,
const RuleParams *params,
dbus_int32_t *toggles,
dbus_bool_t *log,
const BusPolicyRule **matched_rule)
check_rules_list_with_score (DBusList *rules,
int score,
const RuleParams *params,
dbus_int32_t *toggles,
dbus_bool_t *log,
const BusPolicyRule **matched_rule,
dbus_bool_t break_on_first_match)
{
dbus_int32_t local_toggles;
dbus_bool_t local_log;
const BusPolicyRule *local_matched_rule;
const BusPolicyRulesWithScore *rules_list;
rules_list = _dbus_hash_table_lookup_string (rules, name);
if (rules_list == NULL || rules_list->score <= score)
return score;
local_toggles = 0;
check_rules_list (rules_list->rules,
check_rules_list (rules,
FALSE,
params,
&local_toggles,
&local_log,
&local_matched_rule,
TRUE);
break_on_first_match);
if (local_toggles > 0)
{
@ -1368,8 +1390,28 @@ check_rules_for_name (DBusHashTable *rules,
return score;
}
static int
check_rules_for_name (DBusHashTable *rules,
const char *name,
int score,
const RuleParams *params,
dbus_int32_t *toggles,
dbus_bool_t *log,
const BusPolicyRule **matched_rule)
{
const BusPolicyRulesWithScore *rules_list;
rules_list = _dbus_hash_table_lookup_string (rules, name);
if (rules_list == NULL || rules_list->score <= score)
return score;
return check_rules_list_with_score (rules_list->rules, score, params, toggles, log, matched_rule, TRUE);
}
static int
find_and_check_rules_for_name (DBusHashTable *rules,
DBusList *prefix_rules,
const char *c_str,
int score,
const RuleParams *params,
@ -1377,41 +1419,22 @@ find_and_check_rules_for_name (DBusHashTable *rules,
dbus_bool_t *log,
const BusPolicyRule **matched_rule)
{
char name[DBUS_MAXIMUM_NAME_LENGTH+2];
int pos = strlen(c_str);
score = check_rules_for_name (rules, c_str,
score, params,
toggles, log,
matched_rule);
_dbus_assert (pos <= DBUS_MAXIMUM_NAME_LENGTH);
strncpy (name, c_str, sizeof(name)-1);
/*
* To check 'prefix' rules we not only need to check a name,
* but also every prefix of the name. For example,
* if name is 'foo.bar.baz.qux' we need to check rules for:
* - foo.bar.baz.qux
* - foo.bar.baz
* - foo.bar
* - foo
*/
while (pos > 0)
{
score = check_rules_for_name (rules, name,
score, params,
toggles, log,
matched_rule);
/* strip the last component for next iteration */
while (pos > 0 && name[pos] != '.')
pos--;
name[pos] = 0;
}
score = check_rules_list_with_score (prefix_rules,
score, params,
toggles, log,
matched_rule, FALSE);
return score;
}
static dbus_bool_t
find_and_check_rules (DBusHashTable *rules,
DBusList *prefix_rules,
const RuleParams *params,
dbus_int32_t *toggles,
dbus_bool_t *log)
@ -1442,7 +1465,7 @@ find_and_check_rules (DBusHashTable *rules,
if (name[0] == ':')
continue;
score = find_and_check_rules_for_name (rules, name, score,
score = find_and_check_rules_for_name (rules, prefix_rules, name, score,
params, toggles, log, &matched_rule);
}
}
@ -1457,12 +1480,12 @@ find_and_check_rules (DBusHashTable *rules,
rule_target_name = dbus_message_get_sender (params->u.sr.message);
if (rule_target_name != NULL)
score = find_and_check_rules_for_name (rules, rule_target_name, score,
score = find_and_check_rules_for_name (rules, prefix_rules, rule_target_name, score,
params, toggles, log, &matched_rule);
}
}
else
score = find_and_check_rules_for_name (rules, _dbus_string_get_const_data(params->u.own.name),
score = find_and_check_rules_for_name (rules, prefix_rules, _dbus_string_get_const_data(params->u.own.name),
score, params, toggles, log, &matched_rule);
/* check also wildcard rules */
@ -1486,6 +1509,7 @@ check_policy (BusClientPolicy *policy,
*toggles = 0;
allowed = find_and_check_rules (policy->policy->default_rules_by_name,
policy->policy->default_prefix_rules,
params,
toggles,
log);
@ -1625,6 +1649,7 @@ bus_policy_check_can_own (BusPolicy *policy,
params.u.own.name = service_name;
return find_and_check_rules (policy->default_rules_by_name,
policy->default_prefix_rules,
&params,
NULL,
NULL);