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 */
new_parsed_policy(void)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 */
remove_comment(char * line)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 */
remove_trailing_spaces(char * line)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 */
parse_version(char * ver,struct ipe_parsed_policy * p)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 */
parse_header(char * line,struct ipe_parsed_policy * p)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 */
token_default(char * token)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 */
free_rule(struct ipe_rule * r)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 */
parse_operation(char * t)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 */
parse_action(char * t)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 */
parse_property(char * t,struct ipe_rule * r)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 */
parse_rule(char * line,struct ipe_parsed_policy * p)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 */
ipe_free_parsed_policy(struct ipe_parsed_policy * p)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 */
validate_policy(const struct ipe_parsed_policy * p)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 */
ipe_parse_policy(struct ipe_policy * p)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