Skip to content

Commit cab6eb5

Browse files
tyhicksgregkh
authored andcommitted
ima: Pre-parse the list of keyrings in a KEY_CHECK rule
[ Upstream commit 176377d ] The ima_keyrings buffer was used as a work buffer for strsep()-based parsing of the "keyrings=" option of an IMA policy rule. This parsing was re-performed each time an asymmetric key was added to a kernel keyring for each loaded policy rule that contained a "keyrings=" option. An example rule specifying this option is: measure func=KEY_CHECK keyrings=a|b|c The rule says to measure asymmetric keys added to any of the kernel keyrings named "a", "b", or "c". The size of the buffer size was equal to the size of the largest "keyrings=" value seen in a previously loaded rule (5 + 1 for the NUL-terminator in the previous example) and the buffer was pre-allocated at the time of policy load. The pre-allocated buffer approach suffered from a couple bugs: 1) There was no locking around the use of the buffer so concurrent key add operations, to two different keyrings, would result in the strsep() loop of ima_match_keyring() to modify the buffer at the same time. This resulted in unexpected results from ima_match_keyring() and, therefore, could cause unintended keys to be measured or keys to not be measured when IMA policy intended for them to be measured. 2) If the kstrdup() that initialized entry->keyrings in ima_parse_rule() failed, the ima_keyrings buffer was freed and set to NULL even when a valid KEY_CHECK rule was previously loaded. The next KEY_CHECK event would trigger a call to strcpy() with a NULL destination pointer and crash the kernel. Remove the need for a pre-allocated global buffer by parsing the list of keyrings in a KEY_CHECK rule at the time of policy load. The ima_rule_entry will contain an array of string pointers which point to the name of each keyring specified in the rule. No string processing needs to happen at the time of asymmetric key add so iterating through the list and doing a string comparison is all that's required at the time of policy check. In the process of changing how the "keyrings=" policy option is handled, a couple additional bugs were fixed: 1) The rule parser accepted rules containing invalid "keyrings=" values such as "a|b||c", "a|b|", or simply "|". 2) The /sys/kernel/security/ima/policy file did not display the entire "keyrings=" value if the list of keyrings was longer than what could fit in the fixed size tbuf buffer in ima_policy_show(). Fixes: 5c7bac9 ("IMA: pre-allocate buffer to hold keyrings string") Fixes: 2b60c0e ("IMA: Read keyrings= option from the IMA policy") Signed-off-by: Tyler Hicks <tyhicks@linux.microsoft.com> Reviewed-by: Lakshmi Ramasubramanian <nramas@linux.microsoft.com> Reviewed-by: Nayna Jain <nayna@linux.ibm.com> Signed-off-by: Mimi Zohar <zohar@linux.ibm.com> Signed-off-by: Sasha Levin <sashal@kernel.org>
1 parent d4b8568 commit cab6eb5

1 file changed

Lines changed: 93 additions & 45 deletions

File tree

security/integrity/ima/ima_policy.c

Lines changed: 93 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,11 @@ enum policy_types { ORIGINAL_TCB = 1, DEFAULT_TCB };
5959

6060
enum policy_rule_list { IMA_DEFAULT_POLICY = 1, IMA_CUSTOM_POLICY };
6161

62+
struct ima_rule_opt_list {
63+
size_t count;
64+
char *items[];
65+
};
66+
6267
struct ima_rule_entry {
6368
struct list_head list;
6469
int action;
@@ -78,7 +83,7 @@ struct ima_rule_entry {
7883
int type; /* audit type */
7984
} lsm[MAX_LSM_RULES];
8085
char *fsname;
81-
char *keyrings; /* Measure keys added to these keyrings */
86+
struct ima_rule_opt_list *keyrings; /* Measure keys added to these keyrings */
8287
struct ima_template_desc *template;
8388
};
8489

@@ -206,10 +211,6 @@ static LIST_HEAD(ima_policy_rules);
206211
static LIST_HEAD(ima_temp_rules);
207212
static struct list_head *ima_rules = &ima_default_rules;
208213

209-
/* Pre-allocated buffer used for matching keyrings. */
210-
static char *ima_keyrings;
211-
static size_t ima_keyrings_len;
212-
213214
static int ima_policy __initdata;
214215

215216
static int __init default_measure_policy_setup(char *str)
@@ -253,6 +254,72 @@ static int __init default_appraise_policy_setup(char *str)
253254
}
254255
__setup("ima_appraise_tcb", default_appraise_policy_setup);
255256

257+
static struct ima_rule_opt_list *ima_alloc_rule_opt_list(const substring_t *src)
258+
{
259+
struct ima_rule_opt_list *opt_list;
260+
size_t count = 0;
261+
char *src_copy;
262+
char *cur, *next;
263+
size_t i;
264+
265+
src_copy = match_strdup(src);
266+
if (!src_copy)
267+
return ERR_PTR(-ENOMEM);
268+
269+
next = src_copy;
270+
while ((cur = strsep(&next, "|"))) {
271+
/* Don't accept an empty list item */
272+
if (!(*cur)) {
273+
kfree(src_copy);
274+
return ERR_PTR(-EINVAL);
275+
}
276+
count++;
277+
}
278+
279+
/* Don't accept an empty list */
280+
if (!count) {
281+
kfree(src_copy);
282+
return ERR_PTR(-EINVAL);
283+
}
284+
285+
opt_list = kzalloc(struct_size(opt_list, items, count), GFP_KERNEL);
286+
if (!opt_list) {
287+
kfree(src_copy);
288+
return ERR_PTR(-ENOMEM);
289+
}
290+
291+
/*
292+
* strsep() has already replaced all instances of '|' with '\0',
293+
* leaving a byte sequence of NUL-terminated strings. Reference each
294+
* string with the array of items.
295+
*
296+
* IMPORTANT: Ownership of the allocated buffer is transferred from
297+
* src_copy to the first element in the items array. To free the
298+
* buffer, kfree() must only be called on the first element of the
299+
* array.
300+
*/
301+
for (i = 0, cur = src_copy; i < count; i++) {
302+
opt_list->items[i] = cur;
303+
cur = strchr(cur, '\0') + 1;
304+
}
305+
opt_list->count = count;
306+
307+
return opt_list;
308+
}
309+
310+
static void ima_free_rule_opt_list(struct ima_rule_opt_list *opt_list)
311+
{
312+
if (!opt_list)
313+
return;
314+
315+
if (opt_list->count) {
316+
kfree(opt_list->items[0]);
317+
opt_list->count = 0;
318+
}
319+
320+
kfree(opt_list);
321+
}
322+
256323
static void ima_lsm_free_rule(struct ima_rule_entry *entry)
257324
{
258325
int i;
@@ -274,7 +341,7 @@ static void ima_free_rule(struct ima_rule_entry *entry)
274341
* the defined_templates list and cannot be freed here
275342
*/
276343
kfree(entry->fsname);
277-
kfree(entry->keyrings);
344+
ima_free_rule_opt_list(entry->keyrings);
278345
ima_lsm_free_rule(entry);
279346
kfree(entry);
280347
}
@@ -394,8 +461,8 @@ int ima_lsm_policy_change(struct notifier_block *nb, unsigned long event,
394461
static bool ima_match_keyring(struct ima_rule_entry *rule,
395462
const char *keyring, const struct cred *cred)
396463
{
397-
char *next_keyring, *keyrings_ptr;
398464
bool matched = false;
465+
size_t i;
399466

400467
if ((rule->flags & IMA_UID) && !rule->uid_op(cred->uid, rule->uid))
401468
return false;
@@ -406,15 +473,8 @@ static bool ima_match_keyring(struct ima_rule_entry *rule,
406473
if (!keyring)
407474
return false;
408475

409-
strcpy(ima_keyrings, rule->keyrings);
410-
411-
/*
412-
* "keyrings=" is specified in the policy in the format below:
413-
* keyrings=.builtin_trusted_keys|.ima|.evm
414-
*/
415-
keyrings_ptr = ima_keyrings;
416-
while ((next_keyring = strsep(&keyrings_ptr, "|")) != NULL) {
417-
if (!strcmp(next_keyring, keyring)) {
476+
for (i = 0; i < rule->keyrings->count; i++) {
477+
if (!strcmp(rule->keyrings->items[i], keyring)) {
418478
matched = true;
419479
break;
420480
}
@@ -1065,7 +1125,6 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
10651125
bool uid_token;
10661126
struct ima_template_desc *template_desc;
10671127
int result = 0;
1068-
size_t keyrings_len;
10691128

10701129
ab = integrity_audit_log_start(audit_context(), GFP_KERNEL,
10711130
AUDIT_INTEGRITY_POLICY_RULE);
@@ -1231,37 +1290,18 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
12311290
case Opt_keyrings:
12321291
ima_log_string(ab, "keyrings", args[0].from);
12331292

1234-
keyrings_len = strlen(args[0].from) + 1;
1235-
1236-
if ((entry->keyrings) ||
1237-
(keyrings_len < 2)) {
1293+
if (entry->keyrings) {
12381294
result = -EINVAL;
12391295
break;
12401296
}
12411297

1242-
if (keyrings_len > ima_keyrings_len) {
1243-
char *tmpbuf;
1244-
1245-
tmpbuf = krealloc(ima_keyrings, keyrings_len,
1246-
GFP_KERNEL);
1247-
if (!tmpbuf) {
1248-
result = -ENOMEM;
1249-
break;
1250-
}
1251-
1252-
ima_keyrings = tmpbuf;
1253-
ima_keyrings_len = keyrings_len;
1254-
}
1255-
1256-
entry->keyrings = kstrdup(args[0].from, GFP_KERNEL);
1257-
if (!entry->keyrings) {
1258-
kfree(ima_keyrings);
1259-
ima_keyrings = NULL;
1260-
ima_keyrings_len = 0;
1261-
result = -ENOMEM;
1298+
entry->keyrings = ima_alloc_rule_opt_list(args);
1299+
if (IS_ERR(entry->keyrings)) {
1300+
result = PTR_ERR(entry->keyrings);
1301+
entry->keyrings = NULL;
12621302
break;
12631303
}
1264-
result = 0;
1304+
12651305
entry->flags |= IMA_KEYRINGS;
12661306
break;
12671307
case Opt_fsuuid:
@@ -1574,6 +1614,15 @@ static void policy_func_show(struct seq_file *m, enum ima_hooks func)
15741614
seq_printf(m, "func=%d ", func);
15751615
}
15761616

1617+
static void ima_show_rule_opt_list(struct seq_file *m,
1618+
const struct ima_rule_opt_list *opt_list)
1619+
{
1620+
size_t i;
1621+
1622+
for (i = 0; i < opt_list->count; i++)
1623+
seq_printf(m, "%s%s", i ? "|" : "", opt_list->items[i]);
1624+
}
1625+
15771626
int ima_policy_show(struct seq_file *m, void *v)
15781627
{
15791628
struct ima_rule_entry *entry = v;
@@ -1630,9 +1679,8 @@ int ima_policy_show(struct seq_file *m, void *v)
16301679
}
16311680

16321681
if (entry->flags & IMA_KEYRINGS) {
1633-
if (entry->keyrings != NULL)
1634-
snprintf(tbuf, sizeof(tbuf), "%s", entry->keyrings);
1635-
seq_printf(m, pt(Opt_keyrings), tbuf);
1682+
seq_puts(m, "keyrings=");
1683+
ima_show_rule_opt_list(m, entry->keyrings);
16361684
seq_puts(m, " ");
16371685
}
16381686

0 commit comments

Comments
 (0)