Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

ipe: add policy parser

IPE's interpretation of the what the user trusts is accomplished through
its policy. IPE's design is to not provide support for a single trust
provider, but to support multiple providers to enable the end-user to
choose the best one to seek their needs.

This requires the policy to be rather flexible and modular so that
integrity providers, like fs-verity, dm-verity, or some other system,
can plug into the policy with minimal code changes.

Signed-off-by: Deven Bowers <deven.desai@linux.microsoft.com>
Signed-off-by: Fan Wu <wufan@linux.microsoft.com>
[PM: added NULL check in parse_rule() as discussed]
Signed-off-by: Paul Moore <paul@paul-moore.com>

authored by

Deven Bowers and committed by
Paul Moore
54a88cd2 03115077

+697
+2
security/ipe/Makefile
··· 7 7 8 8 obj-$(CONFIG_SECURITY_IPE) += \ 9 9 ipe.o \ 10 + policy.o \ 11 + policy_parser.o \
+103
security/ipe/policy.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Copyright (C) 2020-2024 Microsoft Corporation. All rights reserved. 4 + */ 5 + 6 + #include <linux/errno.h> 7 + #include <linux/verification.h> 8 + 9 + #include "ipe.h" 10 + #include "policy.h" 11 + #include "policy_parser.h" 12 + 13 + /** 14 + * ipe_free_policy() - Deallocate a given IPE policy. 15 + * @p: Supplies the policy to free. 16 + * 17 + * Safe to call on IS_ERR/NULL. 18 + */ 19 + void ipe_free_policy(struct ipe_policy *p) 20 + { 21 + if (IS_ERR_OR_NULL(p)) 22 + return; 23 + 24 + ipe_free_parsed_policy(p->parsed); 25 + /* 26 + * p->text is allocated only when p->pkcs7 is not NULL 27 + * otherwise it points to the plaintext data inside the pkcs7 28 + */ 29 + if (!p->pkcs7) 30 + kfree(p->text); 31 + kfree(p->pkcs7); 32 + kfree(p); 33 + } 34 + 35 + static int set_pkcs7_data(void *ctx, const void *data, size_t len, 36 + size_t asn1hdrlen __always_unused) 37 + { 38 + struct ipe_policy *p = ctx; 39 + 40 + p->text = (const char *)data; 41 + p->textlen = len; 42 + 43 + return 0; 44 + } 45 + 46 + /** 47 + * ipe_new_policy() - Allocate and parse an ipe_policy structure. 48 + * 49 + * @text: Supplies a pointer to the plain-text policy to parse. 50 + * @textlen: Supplies the length of @text. 51 + * @pkcs7: Supplies a pointer to a pkcs7-signed IPE policy. 52 + * @pkcs7len: Supplies the length of @pkcs7. 53 + * 54 + * @text/@textlen Should be NULL/0 if @pkcs7/@pkcs7len is set. 55 + * 56 + * Return: 57 + * * a pointer to the ipe_policy structure - Success 58 + * * %-EBADMSG - Policy is invalid 59 + * * %-ENOMEM - Out of memory (OOM) 60 + * * %-ERANGE - Policy version number overflow 61 + * * %-EINVAL - Policy version parsing error 62 + */ 63 + struct ipe_policy *ipe_new_policy(const char *text, size_t textlen, 64 + const char *pkcs7, size_t pkcs7len) 65 + { 66 + struct ipe_policy *new = NULL; 67 + int rc = 0; 68 + 69 + new = kzalloc(sizeof(*new), GFP_KERNEL); 70 + if (!new) 71 + return ERR_PTR(-ENOMEM); 72 + 73 + if (!text) { 74 + new->pkcs7len = pkcs7len; 75 + new->pkcs7 = kmemdup(pkcs7, pkcs7len, GFP_KERNEL); 76 + if (!new->pkcs7) { 77 + rc = -ENOMEM; 78 + goto err; 79 + } 80 + 81 + rc = verify_pkcs7_signature(NULL, 0, new->pkcs7, pkcs7len, NULL, 82 + VERIFYING_UNSPECIFIED_SIGNATURE, 83 + set_pkcs7_data, new); 84 + if (rc) 85 + goto err; 86 + } else { 87 + new->textlen = textlen; 88 + new->text = kstrdup(text, GFP_KERNEL); 89 + if (!new->text) { 90 + rc = -ENOMEM; 91 + goto err; 92 + } 93 + } 94 + 95 + rc = ipe_parse_policy(new); 96 + if (rc) 97 + goto err; 98 + 99 + return new; 100 + err: 101 + ipe_free_policy(new); 102 + return ERR_PTR(rc); 103 + }
+83
security/ipe/policy.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + /* 3 + * Copyright (C) 2020-2024 Microsoft Corporation. All rights reserved. 4 + */ 5 + #ifndef _IPE_POLICY_H 6 + #define _IPE_POLICY_H 7 + 8 + #include <linux/list.h> 9 + #include <linux/types.h> 10 + 11 + enum ipe_op_type { 12 + IPE_OP_EXEC = 0, 13 + IPE_OP_FIRMWARE, 14 + IPE_OP_KERNEL_MODULE, 15 + IPE_OP_KEXEC_IMAGE, 16 + IPE_OP_KEXEC_INITRAMFS, 17 + IPE_OP_POLICY, 18 + IPE_OP_X509, 19 + __IPE_OP_MAX, 20 + }; 21 + 22 + #define IPE_OP_INVALID __IPE_OP_MAX 23 + 24 + enum ipe_action_type { 25 + IPE_ACTION_ALLOW = 0, 26 + IPE_ACTION_DENY, 27 + __IPE_ACTION_MAX 28 + }; 29 + 30 + #define IPE_ACTION_INVALID __IPE_ACTION_MAX 31 + 32 + enum ipe_prop_type { 33 + __IPE_PROP_MAX 34 + }; 35 + 36 + #define IPE_PROP_INVALID __IPE_PROP_MAX 37 + 38 + struct ipe_prop { 39 + struct list_head next; 40 + enum ipe_prop_type type; 41 + void *value; 42 + }; 43 + 44 + struct ipe_rule { 45 + enum ipe_op_type op; 46 + enum ipe_action_type action; 47 + struct list_head props; 48 + struct list_head next; 49 + }; 50 + 51 + struct ipe_op_table { 52 + struct list_head rules; 53 + enum ipe_action_type default_action; 54 + }; 55 + 56 + struct ipe_parsed_policy { 57 + const char *name; 58 + struct { 59 + u16 major; 60 + u16 minor; 61 + u16 rev; 62 + } version; 63 + 64 + enum ipe_action_type global_default_action; 65 + 66 + struct ipe_op_table rules[__IPE_OP_MAX]; 67 + }; 68 + 69 + struct ipe_policy { 70 + const char *pkcs7; 71 + size_t pkcs7len; 72 + 73 + const char *text; 74 + size_t textlen; 75 + 76 + struct ipe_parsed_policy *parsed; 77 + }; 78 + 79 + struct ipe_policy *ipe_new_policy(const char *text, size_t textlen, 80 + const char *pkcs7, size_t pkcs7len); 81 + void ipe_free_policy(struct ipe_policy *pol); 82 + 83 + #endif /* _IPE_POLICY_H */
+498
security/ipe/policy_parser.c
··· 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 + /** 274 + * parse_property() - Parse a rule property given a token string. 275 + * @t: Supplies the token string to be parsed. 276 + * @r: Supplies the ipe_rule the parsed property will be associated with. 277 + * 278 + * This is a placeholder. The actual function will be introduced in the 279 + * latter commits. 280 + * 281 + * Return: 282 + * * %0 - Success 283 + * * %-ENOMEM - Out of memory (OOM) 284 + * * %-EBADMSG - The supplied token cannot be parsed 285 + */ 286 + static int parse_property(char *t, struct ipe_rule *r) 287 + { 288 + return -EBADMSG; 289 + } 290 + 291 + /** 292 + * parse_rule() - parse a policy rule line. 293 + * @line: Supplies rule line to be parsed. 294 + * @p: Supplies the partial parsed policy. 295 + * 296 + * Return: 297 + * * 0 - Success 298 + * * %-ENOMEM - Out of memory (OOM) 299 + * * %-EBADMSG - Policy syntax error 300 + */ 301 + static int parse_rule(char *line, struct ipe_parsed_policy *p) 302 + { 303 + enum ipe_action_type action = IPE_ACTION_INVALID; 304 + enum ipe_op_type op = IPE_OP_INVALID; 305 + bool is_default_rule = false; 306 + struct ipe_rule *r = NULL; 307 + bool first_token = true; 308 + bool op_parsed = false; 309 + int rc = 0; 310 + char *t; 311 + 312 + if (IS_ERR_OR_NULL(line)) 313 + return -EBADMSG; 314 + 315 + r = kzalloc(sizeof(*r), GFP_KERNEL); 316 + if (!r) 317 + return -ENOMEM; 318 + 319 + INIT_LIST_HEAD(&r->next); 320 + INIT_LIST_HEAD(&r->props); 321 + 322 + while (t = strsep(&line, IPE_POLICY_DELIM), line) { 323 + if (*t == '\0') 324 + continue; 325 + if (first_token && token_default(t)) { 326 + is_default_rule = true; 327 + } else { 328 + if (!op_parsed) { 329 + op = parse_operation(t); 330 + if (op == IPE_OP_INVALID) 331 + rc = -EBADMSG; 332 + else 333 + op_parsed = true; 334 + } else { 335 + rc = parse_property(t, r); 336 + } 337 + } 338 + 339 + if (rc) 340 + goto err; 341 + first_token = false; 342 + } 343 + 344 + action = parse_action(t); 345 + if (action == IPE_ACTION_INVALID) { 346 + rc = -EBADMSG; 347 + goto err; 348 + } 349 + 350 + if (is_default_rule) { 351 + if (!list_empty(&r->props)) { 352 + rc = -EBADMSG; 353 + } else if (op == IPE_OP_INVALID) { 354 + if (p->global_default_action != IPE_ACTION_INVALID) 355 + rc = -EBADMSG; 356 + else 357 + p->global_default_action = action; 358 + } else { 359 + if (p->rules[op].default_action != IPE_ACTION_INVALID) 360 + rc = -EBADMSG; 361 + else 362 + p->rules[op].default_action = action; 363 + } 364 + } else if (op != IPE_OP_INVALID && action != IPE_ACTION_INVALID) { 365 + r->op = op; 366 + r->action = action; 367 + } else { 368 + rc = -EBADMSG; 369 + } 370 + 371 + if (rc) 372 + goto err; 373 + if (!is_default_rule) 374 + list_add_tail(&r->next, &p->rules[op].rules); 375 + else 376 + free_rule(r); 377 + 378 + return rc; 379 + err: 380 + free_rule(r); 381 + return rc; 382 + } 383 + 384 + /** 385 + * ipe_free_parsed_policy() - free a parsed policy structure. 386 + * @p: Supplies the parsed policy. 387 + */ 388 + void ipe_free_parsed_policy(struct ipe_parsed_policy *p) 389 + { 390 + struct ipe_rule *pp, *t; 391 + size_t i = 0; 392 + 393 + if (IS_ERR_OR_NULL(p)) 394 + return; 395 + 396 + for (i = 0; i < ARRAY_SIZE(p->rules); ++i) 397 + list_for_each_entry_safe(pp, t, &p->rules[i].rules, next) { 398 + list_del(&pp->next); 399 + free_rule(pp); 400 + } 401 + 402 + kfree(p->name); 403 + kfree(p); 404 + } 405 + 406 + /** 407 + * validate_policy() - validate a parsed policy. 408 + * @p: Supplies the fully parsed policy. 409 + * 410 + * Given a policy structure that was just parsed, validate that all 411 + * operations have their default rules or a global default rule is set. 412 + * 413 + * Return: 414 + * * %0 - Success 415 + * * %-EBADMSG - Policy is invalid 416 + */ 417 + static int validate_policy(const struct ipe_parsed_policy *p) 418 + { 419 + size_t i = 0; 420 + 421 + if (p->global_default_action != IPE_ACTION_INVALID) 422 + return 0; 423 + 424 + for (i = 0; i < ARRAY_SIZE(p->rules); ++i) { 425 + if (p->rules[i].default_action == IPE_ACTION_INVALID) 426 + return -EBADMSG; 427 + } 428 + 429 + return 0; 430 + } 431 + 432 + /** 433 + * ipe_parse_policy() - Given a string, parse the string into an IPE policy. 434 + * @p: partially filled ipe_policy structure to populate with the result. 435 + * it must have text and textlen set. 436 + * 437 + * Return: 438 + * * %0 - Success 439 + * * %-EBADMSG - Policy is invalid 440 + * * %-ENOMEM - Out of Memory 441 + * * %-ERANGE - Policy version number overflow 442 + * * %-EINVAL - Policy version parsing error 443 + */ 444 + int ipe_parse_policy(struct ipe_policy *p) 445 + { 446 + struct ipe_parsed_policy *pp = NULL; 447 + char *policy = NULL, *dup = NULL; 448 + bool header_parsed = false; 449 + char *line = NULL; 450 + size_t len; 451 + int rc = 0; 452 + 453 + if (!p->textlen) 454 + return -EBADMSG; 455 + 456 + policy = kmemdup_nul(p->text, p->textlen, GFP_KERNEL); 457 + if (!policy) 458 + return -ENOMEM; 459 + dup = policy; 460 + 461 + pp = new_parsed_policy(); 462 + if (IS_ERR(pp)) { 463 + rc = PTR_ERR(pp); 464 + goto out; 465 + } 466 + 467 + while ((line = strsep(&policy, IPE_LINE_DELIM)) != NULL) { 468 + remove_comment(line); 469 + len = remove_trailing_spaces(line); 470 + if (!len) 471 + continue; 472 + 473 + if (!header_parsed) { 474 + rc = parse_header(line, pp); 475 + if (rc) 476 + goto err; 477 + header_parsed = true; 478 + } else { 479 + rc = parse_rule(line, pp); 480 + if (rc) 481 + goto err; 482 + } 483 + } 484 + 485 + if (!header_parsed || validate_policy(pp)) { 486 + rc = -EBADMSG; 487 + goto err; 488 + } 489 + 490 + p->parsed = pp; 491 + 492 + out: 493 + kfree(dup); 494 + return rc; 495 + err: 496 + ipe_free_parsed_policy(pp); 497 + goto out; 498 + }
+11
security/ipe/policy_parser.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + /* 3 + * Copyright (C) 2020-2024 Microsoft Corporation. All rights reserved. 4 + */ 5 + #ifndef _IPE_POLICY_PARSER_H 6 + #define _IPE_POLICY_PARSER_H 7 + 8 + int ipe_parse_policy(struct ipe_policy *p); 9 + void ipe_free_parsed_policy(struct ipe_parsed_policy *p); 10 + 11 + #endif /* _IPE_POLICY_PARSER_H */