Allow the dfa to return its state, which will allow for pair matches. --- security/apparmor/apparmor.h | 7 ++- security/apparmor/inline.h | 2 security/apparmor/match.c | 99 ++++++++++++++++++++++++++++++++++++++----- security/apparmor/match.h | 2 4 files changed, 99 insertions(+), 11 deletions(-) --- a/security/apparmor/apparmor.h +++ b/security/apparmor/apparmor.h @@ -45,7 +45,6 @@ #define AA_SECURE_EXEC_NEEDED 1 - /* Control parameters (0 or 1), settable thru module/boot flags or * via /sys/kernel/security/apparmor/control */ extern int apparmor_complain; @@ -260,5 +259,11 @@ extern void aa_match_free(struct aa_dfa extern int unpack_dfa(struct aa_dfa *dfa, void *blob, size_t size); extern int verify_dfa(struct aa_dfa *dfa); extern unsigned int aa_dfa_match(struct aa_dfa *dfa, const char *str); +extern unsigned int aa_dfa_next_state(struct aa_dfa *dfa, unsigned int start, + const char *str); +extern unsigned int aa_match_state(struct aa_dfa *dfa, unsigned int start, + const char *str, unsigned int *final); +extern unsigned int aa_dfa_null_transition(struct aa_dfa *dfa, + unsigned int start); #endif /* __APPARMOR_H */ --- a/security/apparmor/inline.h +++ b/security/apparmor/inline.h @@ -12,6 +12,8 @@ #include +#include "match.h" + static inline int mediated_filesystem(struct inode *inode) { return !(inode->i_sb->s_flags & MS_NOUSER); --- a/security/apparmor/match.c +++ b/security/apparmor/match.c @@ -208,22 +208,26 @@ void aa_match_free(struct aa_dfa *dfa) } /** - * aa_dfa_match - match @path against @dfa starting in @state - * @dfa: the dfa to match @path against - * @state: the state to start matching in - * @path: the path to match against the dfa + * aa_dfa_next_state - traverse @dfa to find state @str stops at + * @dfa: the dfa to match @str against + * @start: the state of the dfa to start matching in + * @str: the string to match against the dfa * - * aa_dfa_match will match the full path length and return the state it - * finished matching in. The final state is used to look up the accepting - * label. + * aa_dfa_next_state will match @str against the dfa and return the state it + * finished matching in. The final state can be used to look up the accepting + * label, or as the start state of a continuing match. */ -unsigned int aa_dfa_match(struct aa_dfa *dfa, const char *str) +unsigned int aa_dfa_next_state(struct aa_dfa *dfa, unsigned int start, + const char *str) { u16 *def = DEFAULT_TABLE(dfa); u32 *base = BASE_TABLE(dfa); u16 *next = NEXT_TABLE(dfa); u16 *check = CHECK_TABLE(dfa); - unsigned int state = 1, pos; + unsigned int state = start, pos; + + if (state == 0) + return 0; /* current state is , matching character *str */ if (dfa->tables[YYTD_ID_EC - 1]) { @@ -244,5 +248,80 @@ unsigned int aa_dfa_match(struct aa_dfa state = def[state]; } } - return ACCEPT_TABLE(dfa)[state]; + return state; +} + +/** + * aa_dfa_null_transition - step to next state after null character + * @dfa: the dfa to match against + * @start: the state of the dfa to start matching in + * + * aa_dfa_null_transition transitions to the next state after a null + * character which is not used in standard matching and is only + * used to seperate pairs. + */ +unsigned int aa_dfa_null_transition(struct aa_dfa *dfa, unsigned int start) +{ + u16 *def = DEFAULT_TABLE(dfa); + u32 *base = BASE_TABLE(dfa); + u16 *next = NEXT_TABLE(dfa); + u16 *check = CHECK_TABLE(dfa); + unsigned int state = start, pos; + + /* current state is , matching character *str */ + if (dfa->tables[YYTD_ID_EC - 1]) { + u8 *equiv = EQUIV_TABLE(dfa); + pos = base[state] + equiv[0]; + if (check[pos] == state) + state = next[pos]; + else + state = def[state]; + } else { + pos = base[state] + 0; + if (check[pos] == state) + state = next[pos]; + else + state = def[state]; + } + + return state; +} + +/** + * aa_dfa_match - find accept perm for @str in @dfa + * @dfa: the dfa to match @str against + * @str: the string to match against the dfa + * + * aa_dfa_match will match @str and return the accept perms for the + * final state. + */ +unsigned int aa_dfa_match(struct aa_dfa *dfa, const char *str) +{ + return ACCEPT_TABLE(dfa)[aa_dfa_next_state(dfa, DFA_START, str)]; } + +/** + * aa_match_state - find accept perm and state for @str in @dfa + * @dfa: the dfa to match @str against + * @start: the state to start the match from + * @str: the string to match against the dfa + * @final: the state that the match finished in + * + * aa_match_state will match @str and return the accept perms, and @final + * state, the match occured in. + */ +unsigned int aa_match_state(struct aa_dfa *dfa, unsigned int start, + const char *str, unsigned int *final) +{ + unsigned int state; + if (dfa) { + state = aa_dfa_next_state(dfa, start, str); + if (final) + *final = state; + return ACCEPT_TABLE(dfa)[state]; + } + if (final) + *final = 0; + return 0; +} + --- a/security/apparmor/match.h +++ b/security/apparmor/match.h @@ -12,6 +12,8 @@ #ifndef __MATCH_H #define __MATCH_H +#define DFA_START 1 + /** * The format used for transition tables is based on the GNU flex table * file format (--tables-file option; see Table File Format in the flex