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