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

apparmor: add outofband transition and use it in xattr match

There are cases where the a special out of band transition that can
not be triggered by input is useful in separating match conditions
in the dfa encoding.

The null_transition is currently used as an out of band transition
for match conditions that can not contain a \0 in their input
but apparmor needs an out of band transition for cases where
the match condition is allowed to contain any input character.

Achieve this by allowing for an explicit transition out of input
range that can only be triggered by code.

Signed-off-by: John Johansen <john.johansen@canonical.com>

+62 -7
+3 -1
security/apparmor/apparmorfs.c
··· 591 591 592 592 void __aa_bump_ns_revision(struct aa_ns *ns) 593 593 { 594 - WRITE_ONCE(ns->revision, ns->revision + 1); 594 + WRITE_ONCE(ns->revision, READ_ONCE(ns->revision) + 1); 595 595 wake_up_interruptible(&ns->wait); 596 596 } 597 597 ··· 2331 2331 static struct aa_sfs_entry aa_sfs_entry_policy[] = { 2332 2332 AA_SFS_DIR("versions", aa_sfs_entry_versions), 2333 2333 AA_SFS_FILE_BOOLEAN("set_load", 1), 2334 + /* number of out of band transitions supported */ 2335 + AA_SFS_FILE_U64("outofband", MAX_OOB_SUPPORTED), 2334 2336 { } 2335 2337 }; 2336 2338
+9 -4
security/apparmor/domain.c
··· 320 320 might_sleep(); 321 321 322 322 /* transition from exec match to xattr set */ 323 - state = aa_dfa_null_transition(profile->xmatch, state); 324 - 323 + state = aa_dfa_outofband_transition(profile->xmatch, state); 325 324 d = bprm->file->f_path.dentry; 326 325 327 326 for (i = 0; i < profile->xattr_count; i++) { ··· 329 330 if (size >= 0) { 330 331 u32 perm; 331 332 332 - /* Check the xattr value, not just presence */ 333 + /* 334 + * Check the xattr presence before value. This ensure 335 + * that not present xattr can be distinguished from a 0 336 + * length value or rule that matches any value 337 + */ 338 + state = aa_dfa_null_transition(profile->xmatch, state); 339 + /* Check xattr value */ 333 340 state = aa_dfa_match_len(profile->xmatch, state, value, 334 341 size); 335 342 perm = dfa_user_allow(profile->xmatch, state); ··· 345 340 } 346 341 } 347 342 /* transition to next element */ 348 - state = aa_dfa_null_transition(profile->xmatch, state); 343 + state = aa_dfa_outofband_transition(profile->xmatch, state); 349 344 if (size < 0) { 350 345 /* 351 346 * No xattr match, so verify if transition to
+8 -1
security/apparmor/include/match.h
··· 37 37 38 38 #define YYTH_MAGIC 0x1B5E783D 39 39 #define YYTH_FLAG_DIFF_ENCODE 1 40 + #define YYTH_FLAG_OOB_TRANS 2 41 + #define YYTH_FLAGS (YYTH_FLAG_DIFF_ENCODE | YYTH_FLAG_OOB_TRANS) 42 + 43 + #define MAX_OOB_SUPPORTED 1 40 44 41 45 struct table_set_header { 42 46 u32 th_magic; /* YYTH_MAGIC */ ··· 98 94 struct aa_dfa { 99 95 struct kref count; 100 96 u16 flags; 97 + u32 max_oob; 101 98 struct table_header *tables[YYTD_ID_TSIZE]; 102 99 }; 103 100 ··· 132 127 const char *str); 133 128 unsigned int aa_dfa_next(struct aa_dfa *dfa, unsigned int state, 134 129 const char c); 130 + unsigned int aa_dfa_outofband_transition(struct aa_dfa *dfa, 131 + unsigned int state); 135 132 unsigned int aa_dfa_match_until(struct aa_dfa *dfa, unsigned int start, 136 133 const char *str, const char **retpos); 137 134 unsigned int aa_dfa_matchn_until(struct aa_dfa *dfa, unsigned int start, ··· 190 183 #define MARK_DIFF_ENCODE 0x40000000 191 184 #define MATCH_FLAG_OOB_TRANSITION 0x20000000 192 185 #define MATCH_FLAGS_MASK 0xff000000 193 - #define MATCH_FLAGS_VALID MATCH_FLAG_DIFF_ENCODE 186 + #define MATCH_FLAGS_VALID (MATCH_FLAG_DIFF_ENCODE | MATCH_FLAG_OOB_TRANSITION) 194 187 #define MATCH_FLAGS_INVALID (MATCH_FLAGS_MASK & ~MATCH_FLAGS_VALID) 195 188 196 189 #endif /* __AA_MATCH_H */
+42 -1
security/apparmor/match.c
··· 212 212 goto out; 213 213 } 214 214 } 215 + if ((BASE_TABLE(dfa)[i] & MATCH_FLAG_OOB_TRANSITION)) { 216 + if (base_idx(BASE_TABLE(dfa)[i]) < dfa->max_oob) { 217 + pr_err("AppArmor DFA out of bad transition out of range"); 218 + goto out; 219 + } 220 + if (!(dfa->flags & YYTH_FLAG_OOB_TRANS)) { 221 + pr_err("AppArmor DFA out of bad transition state without header flag"); 222 + goto out; 223 + } 224 + } 215 225 if (base_idx(BASE_TABLE(dfa)[i]) + 255 >= trans_count) { 216 226 pr_err("AppArmor DFA next/check upper bounds error\n"); 217 227 goto out; ··· 324 314 goto fail; 325 315 326 316 dfa->flags = ntohs(*(__be16 *) (data + 12)); 327 - if (dfa->flags != 0 && dfa->flags != YYTH_FLAG_DIFF_ENCODE) 317 + if (dfa->flags & ~(YYTH_FLAGS)) 328 318 goto fail; 319 + 320 + /* 321 + * TODO: needed for dfa to support more than 1 oob 322 + * if (dfa->flags & YYTH_FLAGS_OOB_TRANS) { 323 + * if (hsize < 16 + 4) 324 + * goto fail; 325 + * dfa->max_oob = ntol(*(__be32 *) (data + 16)); 326 + * if (dfa->max <= MAX_OOB_SUPPORTED) { 327 + * pr_err("AppArmor DFA OOB greater than supported\n"); 328 + * goto fail; 329 + * } 330 + * } 331 + */ 332 + dfa->max_oob = 1; 329 333 330 334 data += hsize; 331 335 size -= hsize; ··· 525 501 match_char(state, def, base, next, check, equiv[(u8) c]); 526 502 } else 527 503 match_char(state, def, base, next, check, (u8) c); 504 + 505 + return state; 506 + } 507 + 508 + unsigned int aa_dfa_outofband_transition(struct aa_dfa *dfa, unsigned int state) 509 + { 510 + u16 *def = DEFAULT_TABLE(dfa); 511 + u32 *base = BASE_TABLE(dfa); 512 + u16 *next = NEXT_TABLE(dfa); 513 + u16 *check = CHECK_TABLE(dfa); 514 + u32 b = (base)[(state)]; 515 + 516 + if (!(b & MATCH_FLAG_OOB_TRANSITION)) 517 + return DFA_NOMATCH; 518 + 519 + /* No Equivalence class remapping for outofband transitions */ 520 + match_char(state, def, base, next, check, -1); 528 521 529 522 return state; 530 523 }