~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/security/ipe/policy_parser.c

Version: ~ [ linux-6.12-rc7 ] ~ [ linux-6.11.7 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.60 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.116 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.171 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.229 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.285 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.323 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.12 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  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 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php