From 1d0d1fd0c293de091d656abbed294a6ecd440d9a Mon Sep 17 00:00:00 2001 From: John Johansen Date: Sun, 23 Apr 2023 11:56:00 -0700 Subject: [PATCH] parser: and prompt-compat control flag Allow contronling which prompt compat mode fallback is used. Signed-off-by: John Johansen --- parser/parser.h | 9 ++++++++ parser/parser_common.c | 43 +++++++++++++++++++++++++++++++++++++++ parser/parser_interface.c | 4 ++-- parser/parser_main.c | 27 ++++++++++++++++++++++++ parser/parser_regex.c | 13 ++++++------ 5 files changed, 87 insertions(+), 9 deletions(-) diff --git a/parser/parser.h b/parser/parser.h index 979e67889..6695def7b 100644 --- a/parser/parser.h +++ b/parser/parser.h @@ -324,6 +324,10 @@ do { \ /* The parser fills this variable in automatically */ #define PROFILE_NAME_VARIABLE "profile_name" +#define PROMPT_COMPAT_IGNORE 0 +#define PROMPT_COMPAT_PERMSV2 1 +#define PROMPT_COMPAT_PERMSV1 2 + /* from parser_common.c */ extern uint32_t policy_version; extern uint32_t parser_abi_version; @@ -361,6 +365,7 @@ extern int features_supports_flag_error; extern int kernel_supports_oob; extern int kernel_supports_permstable32; extern int kernel_supports_permstable32_v1; +extern int prompt_compat_mode; extern int conf_verbose; extern int conf_quiet; extern int names_only; @@ -376,6 +381,10 @@ extern IncludeCache_t *g_includecache; extern void pwarnf(bool werr, const char *fmt, ...) __attribute__((__format__(__printf__, 2, 3))); extern void common_warn_once(const char *name, const char *msg, const char **warned_name); +bool prompt_compat_mode_supported(int mode); +int default_prompt_compat_mode(); +void print_prompt_compat_mode(FILE *f); + #define pwarn(F, args...) do { if (parseopts.warn & (F)) pwarnf((parseopts.Werror & (F)), ## args); } while (0) diff --git a/parser/parser_common.c b/parser/parser_common.c index 1dc1640f0..3eb7b6ab9 100644 --- a/parser/parser_common.c +++ b/parser/parser_common.c @@ -89,6 +89,7 @@ int features_supports_flag_error = 0; int kernel_supports_oob = 0; /* out of band transitions */ int kernel_supports_permstable32 = 0; /* extended permissions */ int kernel_supports_permstable32_v1 = 0; /* extended permissions */ +int prompt_compat_mode = 0; int conf_verbose = 0; int conf_quiet = 0; int names_only = 0; @@ -168,3 +169,45 @@ void common_warn_once(const char *name, const char *msg, const char **warned_nam if (parseopts.Werror & WARN_RULE_NOT_ENFORCED) exit(1); } + +bool prompt_compat_mode_supported(int mode) +{ + if (mode == PROMPT_COMPAT_PERMSV2 && + (kernel_supports_permstable32 && !kernel_supports_permstable32_v1)) + return true; + else if (mode == PROMPT_COMPAT_PERMSV1 && + (kernel_supports_permstable32_v1)) + return true; + else if (mode == PROMPT_COMPAT_IGNORE) + return true; + + return false; +} + +int default_prompt_compat_mode() +{ + if (prompt_compat_mode_supported(PROMPT_COMPAT_PERMSV2)) + return PROMPT_COMPAT_PERMSV2; + if (prompt_compat_mode_supported(PROMPT_COMPAT_PERMSV1)) + return PROMPT_COMPAT_PERMSV1; + if (prompt_compat_mode_supported(PROMPT_COMPAT_IGNORE)) + return PROMPT_COMPAT_IGNORE; + return PROMPT_COMPAT_IGNORE; +} + +void print_prompt_compat_mode(FILE *f) +{ + switch (prompt_compat_mode) { + case PROMPT_COMPAT_IGNORE: + fprintf(f, "ignore"); + break; + case PROMPT_COMPAT_PERMSV2: + fprintf(f, "permsv2"); + break; + case PROMPT_COMPAT_PERMSV1: + fprintf(f, "permsv1"); + break; + default: + fprintf(f, "Unknown prompt compat mode '%d'", prompt_compat_mode); + } +} diff --git a/parser/parser_interface.c b/parser/parser_interface.c index c9fdd7870..f4abe5bf7 100644 --- a/parser/parser_interface.c +++ b/parser/parser_interface.c @@ -549,14 +549,14 @@ void sd_serialize_profile(std::ostringstream &buf, Profile *profile, // fprintf(stderr, "profile %s: policy xtable\n", profile->name); // TODO: this is dummy exec make dependent on V1 sd_serialize_xtable(buf, profile->exec_table, - kernel_supports_permstable32_v1 ? + profile->uses_prompt_rules && prompt_compat_mode == PROMPT_COMPAT_PERMSV1 ? profile->policy.perms_table.size() : 0); } sd_write_structend(buf); } /* either have a single dfa or lists of different entry types */ - if (kernel_supports_permstable32_v1) { + if (profile->uses_prompt_rules && prompt_compat_mode == PROMPT_COMPAT_PERMSV1) { /* special compat mode to work around verification problem */ sd_serialize_dfa(buf, profile->policy.dfa, profile->policy.size, profile->policy.perms_table); diff --git a/parser/parser_main.c b/parser/parser_main.c index 0f51fc62e..d578c1ff5 100644 --- a/parser/parser_main.c +++ b/parser/parser_main.c @@ -137,6 +137,8 @@ static const char *config_file = "/etc/apparmor/parser.conf"; #define EARLY_ARG_CONFIG_FILE 142 #define ARG_WERROR 143 #define ARG_ESTIMATED_COMPILE_SIZE 144 +#define ARG_PROMPT_COMPAT 145 +#define ARG_PRINT_PROMPT_COMPAT 146 /* Make sure to update BOTH the short and long_options */ static const char *short_options = "ad::f:h::rRVvI:b:BCD:NSm:M:qQn:XKTWkL:O:po:j:"; @@ -192,6 +194,8 @@ struct option long_options[] = { {"override-policy-abi", 1, 0, ARG_OVERRIDE_POLICY_ABI}, /* no short option */ {"config-file", 1, 0, EARLY_ARG_CONFIG_FILE}, /* early option, no short option */ {"estimated-compile-size", 1, 0, ARG_ESTIMATED_COMPILE_SIZE}, /* no short option, not in help */ + {"prompt-compat", 1, 0, ARG_PROMPT_COMPAT}, /* no short option */ + {"print-prompt-compat", 1, 0, ARG_PRINT_PROMPT_COMPAT}, /* no short option */ {NULL, 0, 0, 0}, }; @@ -789,6 +793,26 @@ static int process_arg(int c, char *optarg) estimated_job_size = tmp * mult; } break; + case ARG_PROMPT_COMPAT: + if (strcmp(optarg, "permsv2") == 0) { + prompt_compat_mode = PROMPT_COMPAT_PERMSV1; + } else if (strcmp(optarg, "permsv1") == 0) { + prompt_compat_mode = PROMPT_COMPAT_PERMSV1; + } else if (strcmp(optarg, "default") == 0) { + prompt_compat_mode = default_prompt_compat_mode(); + } else if (strcmp(optarg, "ignore") == 0) { + prompt_compat_mode = PROMPT_COMPAT_IGNORE; + } else { + PERROR("%s: Invalid --prompt-compat option '%s'\n", + progname, optarg); + exit(1); + } + break; + case ARG_PRINT_PROMPT_COMPAT: + fprintf(stderr, "Prompt compat mode: "); + print_prompt_compat_mode(stderr); + fprintf(stderr, "\n"); + break; default: /* 'unrecognized option' error message gets printed by getopt_long() */ exit(1); @@ -1552,6 +1576,9 @@ static bool get_kernel_features(struct aa_features **features) if (kernel_supports_permstable32_v1) { fprintf(stderr, "kernel supports prompt\n"); } + + /* set default prompt_compat_mode to the best that is supported */ + prompt_compat_mode = default_prompt_compat_mode(); if (!kernel_supports_diff_encode) /* clear diff_encode because it is not supported */ parseopts.control &= ~CONTROL_DFA_DIFF_ENCODE; diff --git a/parser/parser_regex.c b/parser/parser_regex.c index b6600266d..968527603 100644 --- a/parser/parser_regex.c +++ b/parser/parser_regex.c @@ -576,8 +576,7 @@ build: * conditional and for just MAY_EXEC can be processed as * none file perms * - * we don't need to build xmatch for buggy permstable32_v1 - * so don't + * we don't need to build xmatch for permstable32, so don't */ prof->xmatch = rules->create_dfablob(&prof->xmatch_size, &prof->xmatch_len, prof->xmatch_perms_table, parseopts, false, kernel_supports_permstable32 && !kernel_supports_permstable32_v1); delete rules; @@ -780,13 +779,13 @@ int process_profile_regex(Profile *prof) /* under permstable32_v1 we weld file and policydb together, so * don't create the file blob here */ - if (prof->dfa.rules->rule_count > 0 && !kernel_supports_permstable32_v1) { + if (prof->dfa.rules->rule_count > 0 && prompt_compat_mode != PROMPT_COMPAT_PERMSV1) { int xmatch_len = 0; //fprintf(stderr, "Creating file DFA %d\n", kernel_supports_permstable32); prof->dfa.dfa = prof->dfa.rules->create_dfablob(&prof->dfa.size, &xmatch_len, prof->dfa.perms_table, parseopts, true, - kernel_supports_permstable32); + prof->uses_prompt_rules && kernel_supports_permstable32); delete prof->dfa.rules; prof->dfa.rules = NULL; if (!prof->dfa.dfa) @@ -1131,7 +1130,7 @@ int process_profile_policydb(Profile *prof) goto out; } - if (kernel_supports_permstable32_v1) { + if (prompt_compat_mode == PROMPT_COMPAT_PERMSV1) { // MUST have file and policy // This requires file rule processing happen first if (!prof->dfa.rules->rule_count) { @@ -1160,13 +1159,13 @@ int process_profile_policydb(Profile *prof) } else if (prof->policy.rules->rule_count > 0 && // yes not needed as covered above, just making sure // this doesn't get messed up in the future - !kernel_supports_permstable32_v1) { + prompt_compat_mode != PROMPT_COMPAT_PERMSV1) { int xmatch_len = 0; prof->policy.dfa = prof->policy.rules->create_dfablob(&prof->policy.size, &xmatch_len, prof->policy.perms_table, parseopts, false, - kernel_supports_permstable32); + prof->uses_prompt_rules && kernel_supports_permstable32); delete prof->policy.rules; prof->policy.rules = NULL;