From d9b3603f48effbf961aa1d346789d0c1586d6f89 Mon Sep 17 00:00:00 2001 From: Georgia Garcia Date: Tue, 25 Feb 2025 19:18:58 -0300 Subject: [PATCH] parser: add support for attach_disconnected.ipc flag The attach_disconnected.ipc flag allows the use of disconnected paths on posix mqueues. This flag is a subset of attach_disconnected, and it does not allow disconnected paths for all files. Signed-off-by: Georgia Garcia --- parser/apparmor.d.pod | 9 +++++++++ parser/parser.h | 3 ++- parser/parser_common.c | 1 + parser/parser_interface.c | 7 +++++++ parser/parser_main.c | 3 +++ parser/parser_variable.c | 35 +++++++++++++++++++++-------------- parser/profile.cc | 2 ++ parser/profile.h | 23 +++++++++++++++++++++++ 8 files changed, 68 insertions(+), 15 deletions(-) diff --git a/parser/apparmor.d.pod b/parser/apparmor.d.pod index a8b27b63a..09de8ccd9 100644 --- a/parser/apparmor.d.pod +++ b/parser/apparmor.d.pod @@ -115,6 +115,7 @@ B = [ 'flags=' ] '(' comma or white space separated list of B = I | I | 'mediate_deleted' | 'attach_disconnected' | 'attach_disconneced.path='I | 'chroot_relative' +| 'attach_disconnected.ipc' | 'attach_disconnected.ipc='I | 'debug' | 'interruptible' | 'kill.signal='I | 'error='I B = (case insensitive error code name starting with 'E'; see errno(3)) @@ -560,6 +561,14 @@ allowed. Its intent is a debug and policy development tool. attach disconnected objects to the supplied path instead of the root of the namespace. +=item B A subset of attach_disconnected, but specific +for IPC namespaces. It allows attaching disconnected IPC paths without having +to allow attaching all types of files. + +=item B=I Like attach_disconnected.ipc, but +attach disconnected posix mqueue to the supplied path instead of the root of +the namespace. + =item B This forces file names to be relative to a chroot and behave as if the chroot is a mount namespace. diff --git a/parser/parser.h b/parser/parser.h index 4c342a8d6..2f63e65cc 100644 --- a/parser/parser.h +++ b/parser/parser.h @@ -189,7 +189,7 @@ extern int preprocess_only; #define PATH_DELEGATE_DELETED 0x20 #define PATH_ATTACH 0x40 #define PATH_NO_ATTACH 0x80 - +#define PATH_IPC_ATTACH 0x100 #ifdef DEBUG @@ -334,6 +334,7 @@ extern int features_supports_io_uring; extern int features_supports_flag_interruptible; extern int features_supports_flag_signal; extern int features_supports_flag_error; +extern int features_supports_flag_disconnected_ipc; extern int kernel_supports_oob; extern int kernel_supports_promptdev; extern int kernel_supports_permstable32; diff --git a/parser/parser_common.c b/parser/parser_common.c index b9f171423..7da975991 100644 --- a/parser/parser_common.c +++ b/parser/parser_common.c @@ -86,6 +86,7 @@ int features_supports_io_uring = 0; /* kernel supports io_uring rules */ int features_supports_flag_interruptible = 0; int features_supports_flag_signal = 0; int features_supports_flag_error = 0; +int features_supports_flag_disconnected_ipc = 0; /* kernel supports disconnected paths for ipc ns */ int kernel_supports_oob = 0; /* out of band transitions */ int kernel_supports_promptdev = 0; /* prompt via audit perms */ int kernel_supports_permstable32 = 0; /* extended permissions */ diff --git a/parser/parser_interface.c b/parser/parser_interface.c index 357d55239..5e4724812 100644 --- a/parser/parser_interface.c +++ b/parser/parser_interface.c @@ -474,6 +474,11 @@ void sd_serialize_profile(std::ostringstream &buf, Profile *profile, "disconnected"); } + if (profile->flags.disconnected_ipc && features_supports_flag_disconnected_ipc) { + sd_write_string(buf, profile->flags.disconnected_ipc, + "disconnected_ipc"); + } + if (profile->flags.signal && features_supports_flag_signal) { sd_write_name(buf, "kill"); sd_write_uint32(buf, profile->flags.signal); @@ -500,6 +505,8 @@ void sd_serialize_profile(std::ostringstream &buf, Profile *profile, flags |= 0x4; if (profile->flags.path & PATH_CHROOT_NSATTACH) flags |= 0x10; + if (profile->flags.path & PATH_IPC_ATTACH) + flags |= 0x20; sd_write_name(buf, "path_flags"); sd_write_uint32(buf, flags); diff --git a/parser/parser_main.c b/parser/parser_main.c index 8178cfadd..8234ec9c3 100644 --- a/parser/parser_main.c +++ b/parser/parser_main.c @@ -991,6 +991,9 @@ void set_supported_features() features_supports_flag_error = features_intersect(kernel_features, policy_features, "policy/profile/error"); + features_supports_flag_disconnected_ipc = features_intersect(kernel_features, + policy_features, + "domain/disconnected.ipc"); } static bool do_print_cache_dir(aa_features *features, int dirfd, const char *path) diff --git a/parser/parser_variable.c b/parser/parser_variable.c index 69ef7a7e3..c04e2a11a 100644 --- a/parser/parser_variable.c +++ b/parser/parser_variable.c @@ -272,6 +272,23 @@ static int process_variables_in_rules(Profile &prof) return 0; } + +static int process_variable_in_attach_disconnected(char **disconnected) +{ + int error = expand_entry_variables(disconnected); + if (error) + return error; + filter_slashes(*disconnected); + // TODO: semantic check should go somewhere else + if ((*disconnected)[0] != '/') + yyerror(_("attach_disconnected path must begin with a /")); + int n = strlen(*disconnected); + // removing trailing / */ + while (n && (*disconnected)[n-1] == '/') + (*disconnected)[--n] = 0; + return error; +} + static int process_variables_in_name(Profile &prof) { /* this needs to be done before alias expansion, ie. altnames are @@ -280,20 +297,10 @@ static int process_variables_in_name(Profile &prof) int error = expand_entry_variables(&prof.name); if (!error && prof.attachment) error = expand_entry_variables(&prof.attachment); - if (!error && prof.flags.disconnected_path) { - error = expand_entry_variables(&prof.flags.disconnected_path); - if (error) - return error; - filter_slashes(prof.flags.disconnected_path); - // TODO: semantic check should go somewhere else - if (prof.flags.disconnected_path[0] != '/') - yyerror(_("attach_disconnected_path value must begin with a /")); - int n = strlen(prof.flags.disconnected_path); - // removing trailing / */ - while (n && prof.flags.disconnected_path[n-1] == '/') - prof.flags.disconnected_path[--n] = 0; - - } + if (!error && prof.flags.disconnected_path) + error = process_variable_in_attach_disconnected(&prof.flags.disconnected_path); + if (!error && prof.flags.disconnected_ipc) + error = process_variable_in_attach_disconnected(&prof.flags.disconnected_ipc); return error; } diff --git a/parser/profile.cc b/parser/profile.cc index d43913581..2f235e587 100644 --- a/parser/profile.cc +++ b/parser/profile.cc @@ -97,6 +97,8 @@ Profile::~Profile() free(attachment); if (flags.disconnected_path) free(flags.disconnected_path); + if (flags.disconnected_ipc) + free(flags.disconnected_ipc); if (ns) free(ns); for (int i = (AA_EXEC_LOCAL >> 10) + 1; i < AA_EXEC_COUNT; i++) diff --git a/parser/profile.h b/parser/profile.h index 370bdaa4b..d245a1376 100644 --- a/parser/profile.h +++ b/parser/profile.h @@ -159,6 +159,7 @@ public: int audit; int path; char *disconnected_path; + char *disconnected_ipc; int signal; int error; @@ -170,6 +171,7 @@ public: audit = 0; path = 0; disconnected_path = NULL; + disconnected_ipc = NULL; signal = 0; error = 0; } @@ -216,6 +218,12 @@ public: yyerror("unknown error code specified for error=\'%s\'\n", str + 6); } else if (strcmp(str, "interruptible") == 0) { flags |= FLAG_INTERRUPTIBLE; + } else if (strcmp(str, "attach_disconnected.ipc") == 0) { + path |= PATH_IPC_ATTACH; + } else if (strncmp(str, "attach_disconnected.ipc=", 24) == 0) { + /* TODO: make this a proper parse */ + path |= PATH_IPC_ATTACH; + disconnected_ipc = strdup(str + 24); } else { yyerror(_("Invalid profile flag: %s."), str); } @@ -237,6 +245,8 @@ public: os << ", kill.signal=" << signal; if (error) os << ", error=" << find_error_name_mapping(error); + if (disconnected_ipc) + os << ", attach_disconnected.ipc=" << disconnected_ipc; if (flags & FLAG_PROMPT_COMPAT) os << ", prompt_dev"; @@ -277,6 +287,9 @@ public: if ((path & (PATH_ATTACH | PATH_NO_ATTACH)) == (PATH_ATTACH | PATH_NO_ATTACH)) yyerror(_("Profile flag attach_disconnected conflicts with no_attach_disconnected")); + if ((path & (PATH_IPC_ATTACH | PATH_NO_ATTACH)) == + (PATH_IPC_ATTACH | PATH_NO_ATTACH)) + yyerror(_("Profile flag attach_disconnected.ipc conflicts with no_attach_disconnected")); if ((path & (PATH_CHROOT_NSATTACH | PATH_CHROOT_NO_ATTACH)) == (PATH_CHROOT_NSATTACH | PATH_CHROOT_NO_ATTACH)) yyerror(_("Profile flag chroot_attach conflicts with chroot_no_attach")); @@ -291,6 +304,16 @@ public: disconnected_path = rhs.disconnected_path; } } + if (rhs.disconnected_ipc) { + if (disconnected_ipc) { + if (strcmp(disconnected_ipc, rhs.disconnected_ipc) != 0) { + yyerror(_("Profile flag attach_disconnected set to conflicting values: '%s' and '%s'"), disconnected_ipc, rhs.disconnected_ipc); + } + // same so do nothing + } else { + disconnected_ipc = rhs.disconnected_ipc; + } + } if (rhs.signal) { if (signal) { if (signal != rhs.signal) {