1 // SPDX-License-Identifier: GPL-2.0 1 2 /* 3 * Copyright (C) 2020-2024 Microsoft Corporati 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 initiali 22 * 23 * Return: 24 * * a pointer to the ipe_parsed_policy struct 25 * * %-ENOMEM 26 */ 27 static struct ipe_parsed_policy *new_parsed_po 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_ 38 39 for (i = 0; i < ARRAY_SIZE(p->rules); 40 t = &p->rules[i]; 41 42 t->default_action = IPE_ACTION 43 INIT_LIST_HEAD(&t->rules); 44 } 45 46 return p; 47 } 48 49 /** 50 * remove_comment() - Truncate all chars follo 51 * 52 * @line: Supplies a policy line string for pr 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 tra 64 * 65 * @line: Supplies a policy line string for pr 66 * 67 * Return: The length of truncated string. 68 */ 69 static size_t remove_trailing_spaces(char *lin 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 parse 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 94 { 95 u16 *const cv[] = { &p->version.major, 96 size_t sep_count = 0; 97 char *token; 98 int rc = 0; 99 100 while ((token = strsep(&ver, ".")) != 101 /* prevent overflow */ 102 if (sep_count >= ARRAY_SIZE(cv 103 return -EBADMSG; 104 105 rc = kstrtou16(token, 10, cv[s 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, "polic 127 {IPE_HEADER_POLICY_VERSION, "polic 128 {__IPE_HEADER_MAX, NULL} 129 }; 130 131 /** 132 * parse_header() - Parse policy header inform 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 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_D 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_ 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 169 if (!p->name) 170 rc = -ENOMEM; 171 break; 172 case IPE_HEADER_POLICY_VERSION 173 ver = match_strdup(&ar 174 if (!ver) { 175 rc = -ENOMEM; 176 break; 177 } 178 rc = parse_version(ver 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 to 198 * @token: Supplies the token string to be com 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 st 211 * @r: Supplies the ipe_rule struct to be free 212 * 213 * Free a ipe_rule struct @r. Note @r must be 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->pro 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=EX 234 {IPE_OP_FIRMWARE, "op=FI 235 {IPE_OP_KERNEL_MODULE, "op=KM 236 {IPE_OP_KEXEC_IMAGE, "op=KE 237 {IPE_OP_KEXEC_INITRAMFS, "op=KE 238 {IPE_OP_POLICY, "op=PO 239 {IPE_OP_X509, "op=X5 240 {IPE_OP_INVALID, NULL} 241 }; 242 243 /** 244 * parse_operation() - Parse the operation typ 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 * 250 { 251 substring_t args[MAX_OPT_ARGS]; 252 253 return match_token(t, operation_tokens 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 give 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 269 { 270 substring_t args[MAX_OPT_ARGS]; 271 272 return match_token(t, action_tokens, a 273 } 274 275 static const match_table_t property_tokens = { 276 {IPE_PROP_BOOT_VERIFIED_FALSE, "boot_ 277 {IPE_PROP_BOOT_VERIFIED_TRUE, "boot_ 278 {IPE_PROP_DMV_ROOTHASH, "dmver 279 {IPE_PROP_DMV_SIG_FALSE, "dmver 280 {IPE_PROP_DMV_SIG_TRUE, "dmver 281 {IPE_PROP_FSV_DIGEST, "fsver 282 {IPE_PROP_FSV_SIG_FALSE, "fsver 283 {IPE_PROP_FSV_SIG_TRUE, "fsver 284 {IPE_PROP_INVALID, NULL} 285 }; 286 287 /** 288 * parse_property() - Parse a rule property gi 289 * @t: Supplies the token string to be parsed. 290 * @r: Supplies the ipe_rule the parsed proper 291 * 292 * This function parses and associates a prope 293 * on a token string. 294 * 295 * Return: 296 * * %0 - Success 297 * * %-ENOMEM - Out of memory (OOM) 298 * * %-EBADMSG - The supplied token cannot be 299 */ 300 static int parse_property(char *t, struct ipe_ 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 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(du 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_p 363 { 364 enum ipe_action_type action = IPE_ACTI 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_DE 384 if (*t == '\0') 385 continue; 386 if (first_token && token_defau 387 is_default_rule = true 388 } else { 389 if (!op_parsed) { 390 op = parse_ope 391 if (op == IPE_ 392 rc = - 393 else 394 op_par 395 } else { 396 rc = parse_pro 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_INVALI 415 if (p->global_default_ 416 rc = -EBADMSG; 417 else 418 p->global_defa 419 } else { 420 if (p->rules[op].defau 421 rc = -EBADMSG; 422 else 423 p->rules[op].d 424 } 425 } else if (op != IPE_OP_INVALID && act 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->ru 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 po 447 * @p: Supplies the parsed policy. 448 */ 449 void ipe_free_parsed_policy(struct ipe_parsed_ 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); 458 list_for_each_entry_safe(pp, t 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 polic 469 * @p: Supplies the fully parsed policy. 470 * 471 * Given a policy structure that was just pars 472 * operations have their default rules or a gl 473 * 474 * Return: 475 * * %0 - Success 476 * * %-EBADMSG - Policy is invalid 477 */ 478 static int validate_policy(const struct ipe_pa 479 { 480 size_t i = 0; 481 482 if (p->global_default_action != IPE_AC 483 return 0; 484 485 for (i = 0; i < ARRAY_SIZE(p->rules); 486 if (p->rules[i].default_action 487 return -EBADMSG; 488 } 489 490 return 0; 491 } 492 493 /** 494 * ipe_parse_policy() - Given a string, parse 495 * @p: partially filled ipe_policy structure t 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 overfl 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->textl 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_LIN 529 remove_comment(line); 530 len = remove_trailing_spaces(l 531 if (!len) 532 continue; 533 534 if (!header_parsed) { 535 rc = parse_header(line 536 if (rc) 537 goto err; 538 header_parsed = true; 539 } else { 540 rc = parse_rule(line, 541 if (rc) 542 goto err; 543 } 544 } 545 546 if (!header_parsed || validate_policy( 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
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.