Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/security/ipe/policy_parser.c
26378 views
1
// SPDX-License-Identifier: GPL-2.0
2
/*
3
* Copyright (C) 2020-2024 Microsoft Corporation. All rights reserved.
4
*/
5
6
#include <linux/err.h>
7
#include <linux/slab.h>
8
#include <linux/parser.h>
9
#include <linux/types.h>
10
#include <linux/ctype.h>
11
12
#include "policy.h"
13
#include "policy_parser.h"
14
#include "digest.h"
15
16
#define START_COMMENT '#'
17
#define IPE_POLICY_DELIM " \t"
18
#define IPE_LINE_DELIM "\n\r"
19
20
/**
21
* new_parsed_policy() - Allocate and initialize a parsed policy.
22
*
23
* Return:
24
* * a pointer to the ipe_parsed_policy structure - Success
25
* * %-ENOMEM - Out of memory (OOM)
26
*/
27
static struct ipe_parsed_policy *new_parsed_policy(void)
28
{
29
struct ipe_parsed_policy *p = NULL;
30
struct ipe_op_table *t = NULL;
31
size_t i = 0;
32
33
p = kzalloc(sizeof(*p), GFP_KERNEL);
34
if (!p)
35
return ERR_PTR(-ENOMEM);
36
37
p->global_default_action = IPE_ACTION_INVALID;
38
39
for (i = 0; i < ARRAY_SIZE(p->rules); ++i) {
40
t = &p->rules[i];
41
42
t->default_action = IPE_ACTION_INVALID;
43
INIT_LIST_HEAD(&t->rules);
44
}
45
46
return p;
47
}
48
49
/**
50
* remove_comment() - Truncate all chars following START_COMMENT in a string.
51
*
52
* @line: Supplies a policy line string for preprocessing.
53
*/
54
static void remove_comment(char *line)
55
{
56
line = strchr(line, START_COMMENT);
57
58
if (line)
59
*line = '\0';
60
}
61
62
/**
63
* remove_trailing_spaces() - Truncate all trailing spaces in a string.
64
*
65
* @line: Supplies a policy line string for preprocessing.
66
*
67
* Return: The length of truncated string.
68
*/
69
static size_t remove_trailing_spaces(char *line)
70
{
71
size_t i = 0;
72
73
i = strlen(line);
74
while (i > 0 && isspace(line[i - 1]))
75
i--;
76
77
line[i] = '\0';
78
79
return i;
80
}
81
82
/**
83
* parse_version() - Parse policy version.
84
* @ver: Supplies a version string to be parsed.
85
* @p: Supplies the partial parsed policy.
86
*
87
* Return:
88
* * %0 - Success
89
* * %-EBADMSG - Version string is invalid
90
* * %-ERANGE - Version number overflow
91
* * %-EINVAL - Parsing error
92
*/
93
static int parse_version(char *ver, struct ipe_parsed_policy *p)
94
{
95
u16 *const cv[] = { &p->version.major, &p->version.minor, &p->version.rev };
96
size_t sep_count = 0;
97
char *token;
98
int rc = 0;
99
100
while ((token = strsep(&ver, ".")) != NULL) {
101
/* prevent overflow */
102
if (sep_count >= ARRAY_SIZE(cv))
103
return -EBADMSG;
104
105
rc = kstrtou16(token, 10, cv[sep_count]);
106
if (rc)
107
return rc;
108
109
++sep_count;
110
}
111
112
/* prevent underflow */
113
if (sep_count != ARRAY_SIZE(cv))
114
return -EBADMSG;
115
116
return 0;
117
}
118
119
enum header_opt {
120
IPE_HEADER_POLICY_NAME = 0,
121
IPE_HEADER_POLICY_VERSION,
122
__IPE_HEADER_MAX
123
};
124
125
static const match_table_t header_tokens = {
126
{IPE_HEADER_POLICY_NAME, "policy_name=%s"},
127
{IPE_HEADER_POLICY_VERSION, "policy_version=%s"},
128
{__IPE_HEADER_MAX, NULL}
129
};
130
131
/**
132
* parse_header() - Parse policy header information.
133
* @line: Supplies header line to be parsed.
134
* @p: Supplies the partial parsed policy.
135
*
136
* Return:
137
* * %0 - Success
138
* * %-EBADMSG - Header string is invalid
139
* * %-ENOMEM - Out of memory (OOM)
140
* * %-ERANGE - Version number overflow
141
* * %-EINVAL - Version parsing error
142
*/
143
static int parse_header(char *line, struct ipe_parsed_policy *p)
144
{
145
substring_t args[MAX_OPT_ARGS];
146
char *t, *ver = NULL;
147
size_t idx = 0;
148
int rc = 0;
149
150
while ((t = strsep(&line, IPE_POLICY_DELIM)) != NULL) {
151
int token;
152
153
if (*t == '\0')
154
continue;
155
if (idx >= __IPE_HEADER_MAX) {
156
rc = -EBADMSG;
157
goto out;
158
}
159
160
token = match_token(t, header_tokens, args);
161
if (token != idx) {
162
rc = -EBADMSG;
163
goto out;
164
}
165
166
switch (token) {
167
case IPE_HEADER_POLICY_NAME:
168
p->name = match_strdup(&args[0]);
169
if (!p->name)
170
rc = -ENOMEM;
171
break;
172
case IPE_HEADER_POLICY_VERSION:
173
ver = match_strdup(&args[0]);
174
if (!ver) {
175
rc = -ENOMEM;
176
break;
177
}
178
rc = parse_version(ver, p);
179
break;
180
default:
181
rc = -EBADMSG;
182
}
183
if (rc)
184
goto out;
185
++idx;
186
}
187
188
if (idx != __IPE_HEADER_MAX)
189
rc = -EBADMSG;
190
191
out:
192
kfree(ver);
193
return rc;
194
}
195
196
/**
197
* token_default() - Determine if the given token is "DEFAULT".
198
* @token: Supplies the token string to be compared.
199
*
200
* Return:
201
* * %false - The token is not "DEFAULT"
202
* * %true - The token is "DEFAULT"
203
*/
204
static bool token_default(char *token)
205
{
206
return !strcmp(token, "DEFAULT");
207
}
208
209
/**
210
* free_rule() - Free the supplied ipe_rule struct.
211
* @r: Supplies the ipe_rule struct to be freed.
212
*
213
* Free a ipe_rule struct @r. Note @r must be removed from any lists before
214
* calling this function.
215
*/
216
static void free_rule(struct ipe_rule *r)
217
{
218
struct ipe_prop *p, *t;
219
220
if (IS_ERR_OR_NULL(r))
221
return;
222
223
list_for_each_entry_safe(p, t, &r->props, next) {
224
list_del(&p->next);
225
ipe_digest_free(p->value);
226
kfree(p);
227
}
228
229
kfree(r);
230
}
231
232
static const match_table_t operation_tokens = {
233
{IPE_OP_EXEC, "op=EXECUTE"},
234
{IPE_OP_FIRMWARE, "op=FIRMWARE"},
235
{IPE_OP_KERNEL_MODULE, "op=KMODULE"},
236
{IPE_OP_KEXEC_IMAGE, "op=KEXEC_IMAGE"},
237
{IPE_OP_KEXEC_INITRAMFS, "op=KEXEC_INITRAMFS"},
238
{IPE_OP_POLICY, "op=POLICY"},
239
{IPE_OP_X509, "op=X509_CERT"},
240
{IPE_OP_INVALID, NULL}
241
};
242
243
/**
244
* parse_operation() - Parse the operation type given a token string.
245
* @t: Supplies the token string to be parsed.
246
*
247
* Return: The parsed operation type.
248
*/
249
static enum ipe_op_type parse_operation(char *t)
250
{
251
substring_t args[MAX_OPT_ARGS];
252
253
return match_token(t, operation_tokens, args);
254
}
255
256
static const match_table_t action_tokens = {
257
{IPE_ACTION_ALLOW, "action=ALLOW"},
258
{IPE_ACTION_DENY, "action=DENY"},
259
{IPE_ACTION_INVALID, NULL}
260
};
261
262
/**
263
* parse_action() - Parse the action type given a token string.
264
* @t: Supplies the token string to be parsed.
265
*
266
* Return: The parsed action type.
267
*/
268
static enum ipe_action_type parse_action(char *t)
269
{
270
substring_t args[MAX_OPT_ARGS];
271
272
return match_token(t, action_tokens, args);
273
}
274
275
static const match_table_t property_tokens = {
276
{IPE_PROP_BOOT_VERIFIED_FALSE, "boot_verified=FALSE"},
277
{IPE_PROP_BOOT_VERIFIED_TRUE, "boot_verified=TRUE"},
278
{IPE_PROP_DMV_ROOTHASH, "dmverity_roothash=%s"},
279
{IPE_PROP_DMV_SIG_FALSE, "dmverity_signature=FALSE"},
280
{IPE_PROP_DMV_SIG_TRUE, "dmverity_signature=TRUE"},
281
{IPE_PROP_FSV_DIGEST, "fsverity_digest=%s"},
282
{IPE_PROP_FSV_SIG_FALSE, "fsverity_signature=FALSE"},
283
{IPE_PROP_FSV_SIG_TRUE, "fsverity_signature=TRUE"},
284
{IPE_PROP_INVALID, NULL}
285
};
286
287
/**
288
* parse_property() - Parse a rule property given a token string.
289
* @t: Supplies the token string to be parsed.
290
* @r: Supplies the ipe_rule the parsed property will be associated with.
291
*
292
* This function parses and associates a property with an IPE rule based
293
* on a token string.
294
*
295
* Return:
296
* * %0 - Success
297
* * %-ENOMEM - Out of memory (OOM)
298
* * %-EBADMSG - The supplied token cannot be parsed
299
*/
300
static int parse_property(char *t, struct ipe_rule *r)
301
{
302
substring_t args[MAX_OPT_ARGS];
303
struct ipe_prop *p = NULL;
304
int rc = 0;
305
int token;
306
char *dup = NULL;
307
308
p = kzalloc(sizeof(*p), GFP_KERNEL);
309
if (!p)
310
return -ENOMEM;
311
312
token = match_token(t, property_tokens, args);
313
314
switch (token) {
315
case IPE_PROP_DMV_ROOTHASH:
316
case IPE_PROP_FSV_DIGEST:
317
dup = match_strdup(&args[0]);
318
if (!dup) {
319
rc = -ENOMEM;
320
goto err;
321
}
322
p->value = ipe_digest_parse(dup);
323
if (IS_ERR(p->value)) {
324
rc = PTR_ERR(p->value);
325
goto err;
326
}
327
fallthrough;
328
case IPE_PROP_BOOT_VERIFIED_FALSE:
329
case IPE_PROP_BOOT_VERIFIED_TRUE:
330
case IPE_PROP_DMV_SIG_FALSE:
331
case IPE_PROP_DMV_SIG_TRUE:
332
case IPE_PROP_FSV_SIG_FALSE:
333
case IPE_PROP_FSV_SIG_TRUE:
334
p->type = token;
335
break;
336
default:
337
rc = -EBADMSG;
338
break;
339
}
340
if (rc)
341
goto err;
342
list_add_tail(&p->next, &r->props);
343
344
out:
345
kfree(dup);
346
return rc;
347
err:
348
kfree(p);
349
goto out;
350
}
351
352
/**
353
* parse_rule() - parse a policy rule line.
354
* @line: Supplies rule line to be parsed.
355
* @p: Supplies the partial parsed policy.
356
*
357
* Return:
358
* * 0 - Success
359
* * %-ENOMEM - Out of memory (OOM)
360
* * %-EBADMSG - Policy syntax error
361
*/
362
static int parse_rule(char *line, struct ipe_parsed_policy *p)
363
{
364
enum ipe_action_type action = IPE_ACTION_INVALID;
365
enum ipe_op_type op = IPE_OP_INVALID;
366
bool is_default_rule = false;
367
struct ipe_rule *r = NULL;
368
bool first_token = true;
369
bool op_parsed = false;
370
int rc = 0;
371
char *t;
372
373
if (IS_ERR_OR_NULL(line))
374
return -EBADMSG;
375
376
r = kzalloc(sizeof(*r), GFP_KERNEL);
377
if (!r)
378
return -ENOMEM;
379
380
INIT_LIST_HEAD(&r->next);
381
INIT_LIST_HEAD(&r->props);
382
383
while (t = strsep(&line, IPE_POLICY_DELIM), line) {
384
if (*t == '\0')
385
continue;
386
if (first_token && token_default(t)) {
387
is_default_rule = true;
388
} else {
389
if (!op_parsed) {
390
op = parse_operation(t);
391
if (op == IPE_OP_INVALID)
392
rc = -EBADMSG;
393
else
394
op_parsed = true;
395
} else {
396
rc = parse_property(t, r);
397
}
398
}
399
400
if (rc)
401
goto err;
402
first_token = false;
403
}
404
405
action = parse_action(t);
406
if (action == IPE_ACTION_INVALID) {
407
rc = -EBADMSG;
408
goto err;
409
}
410
411
if (is_default_rule) {
412
if (!list_empty(&r->props)) {
413
rc = -EBADMSG;
414
} else if (op == IPE_OP_INVALID) {
415
if (p->global_default_action != IPE_ACTION_INVALID)
416
rc = -EBADMSG;
417
else
418
p->global_default_action = action;
419
} else {
420
if (p->rules[op].default_action != IPE_ACTION_INVALID)
421
rc = -EBADMSG;
422
else
423
p->rules[op].default_action = action;
424
}
425
} else if (op != IPE_OP_INVALID && action != IPE_ACTION_INVALID) {
426
r->op = op;
427
r->action = action;
428
} else {
429
rc = -EBADMSG;
430
}
431
432
if (rc)
433
goto err;
434
if (!is_default_rule)
435
list_add_tail(&r->next, &p->rules[op].rules);
436
else
437
free_rule(r);
438
439
return rc;
440
err:
441
free_rule(r);
442
return rc;
443
}
444
445
/**
446
* ipe_free_parsed_policy() - free a parsed policy structure.
447
* @p: Supplies the parsed policy.
448
*/
449
void ipe_free_parsed_policy(struct ipe_parsed_policy *p)
450
{
451
struct ipe_rule *pp, *t;
452
size_t i = 0;
453
454
if (IS_ERR_OR_NULL(p))
455
return;
456
457
for (i = 0; i < ARRAY_SIZE(p->rules); ++i)
458
list_for_each_entry_safe(pp, t, &p->rules[i].rules, next) {
459
list_del(&pp->next);
460
free_rule(pp);
461
}
462
463
kfree(p->name);
464
kfree(p);
465
}
466
467
/**
468
* validate_policy() - validate a parsed policy.
469
* @p: Supplies the fully parsed policy.
470
*
471
* Given a policy structure that was just parsed, validate that all
472
* operations have their default rules or a global default rule is set.
473
*
474
* Return:
475
* * %0 - Success
476
* * %-EBADMSG - Policy is invalid
477
*/
478
static int validate_policy(const struct ipe_parsed_policy *p)
479
{
480
size_t i = 0;
481
482
if (p->global_default_action != IPE_ACTION_INVALID)
483
return 0;
484
485
for (i = 0; i < ARRAY_SIZE(p->rules); ++i) {
486
if (p->rules[i].default_action == IPE_ACTION_INVALID)
487
return -EBADMSG;
488
}
489
490
return 0;
491
}
492
493
/**
494
* ipe_parse_policy() - Given a string, parse the string into an IPE policy.
495
* @p: partially filled ipe_policy structure to populate with the result.
496
* it must have text and textlen set.
497
*
498
* Return:
499
* * %0 - Success
500
* * %-EBADMSG - Policy is invalid
501
* * %-ENOMEM - Out of Memory
502
* * %-ERANGE - Policy version number overflow
503
* * %-EINVAL - Policy version parsing error
504
*/
505
int ipe_parse_policy(struct ipe_policy *p)
506
{
507
struct ipe_parsed_policy *pp = NULL;
508
char *policy = NULL, *dup = NULL;
509
bool header_parsed = false;
510
char *line = NULL;
511
size_t len;
512
int rc = 0;
513
514
if (!p->textlen)
515
return -EBADMSG;
516
517
policy = kmemdup_nul(p->text, p->textlen, GFP_KERNEL);
518
if (!policy)
519
return -ENOMEM;
520
dup = policy;
521
522
pp = new_parsed_policy();
523
if (IS_ERR(pp)) {
524
rc = PTR_ERR(pp);
525
goto out;
526
}
527
528
while ((line = strsep(&policy, IPE_LINE_DELIM)) != NULL) {
529
remove_comment(line);
530
len = remove_trailing_spaces(line);
531
if (!len)
532
continue;
533
534
if (!header_parsed) {
535
rc = parse_header(line, pp);
536
if (rc)
537
goto err;
538
header_parsed = true;
539
} else {
540
rc = parse_rule(line, pp);
541
if (rc)
542
goto err;
543
}
544
}
545
546
if (!header_parsed || validate_policy(pp)) {
547
rc = -EBADMSG;
548
goto err;
549
}
550
551
p->parsed = pp;
552
553
out:
554
kfree(dup);
555
return rc;
556
err:
557
ipe_free_parsed_policy(pp);
558
goto out;
559
}
560
561