This is the first patch of a patchset that ensures that operations in complain mode do not fail with real permission denials upon encountering a disconnected path. Instead, the goal is to audit the disconnected paths by prefixing them with a sentinel character '#', in order to mark them as special disconnected paths that may be unreliable.
This patch implements the sentinel character placement, adds a profile_complain argument to the path lookup functions, and passes the complain-mode information in at all the places where aa_path_perm is called. Signed-off-by: Ryan Lee <ryan....@canonical.com> --- security/apparmor/domain.c | 6 ++++-- security/apparmor/file.c | 6 ++++-- security/apparmor/include/path.h | 4 ++-- security/apparmor/mount.c | 9 ++++++-- security/apparmor/path.c | 37 +++++++++++++++++++++----------- 5 files changed, 42 insertions(+), 20 deletions(-) diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index ce8b057196b6..9703ec2bfa78 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -652,7 +652,8 @@ static struct aa_label *profile_transition(const struct cred *subj_cred, AA_BUG(!bprm); AA_BUG(!buffer); - error = aa_path_name(&bprm->file->f_path, profile->path_flags, buffer, + error = aa_path_name(&bprm->file->f_path, profile->path_flags, + COMPLAIN_MODE(profile), buffer, &name, &info, profile->disconnected); if (error) { if (profile_unconfined(profile) || @@ -767,7 +768,8 @@ static int profile_onexec(const struct cred *subj_cred, return 0; } - error = aa_path_name(&bprm->file->f_path, profile->path_flags, buffer, + error = aa_path_name(&bprm->file->f_path, profile->path_flags, + COMPLAIN_MODE(profile), buffer, &xname, &info, profile->disconnected); if (error) { if (profile_unconfined(profile) || diff --git a/security/apparmor/file.c b/security/apparmor/file.c index a4cdd6cb9af3..5e0cadb75651 100644 --- a/security/apparmor/file.c +++ b/security/apparmor/file.c @@ -299,8 +299,10 @@ static int path_name(const char *op, const struct cred *subj_cred, const char *info = NULL; int error; - error = aa_path_name(path, flags, buffer, name, &info, - labels_profile(label)->disconnected); + // Use the profile var for aa_path_name and reuse it in the for loop + profile = labels_profile(label); + error = aa_path_name(path, flags, COMPLAIN_MODE(profile), + buffer, name, &info, profile->disconnected); if (error) { fn_for_each_confined(label, profile, aa_audit_file(subj_cred, diff --git a/security/apparmor/include/path.h b/security/apparmor/include/path.h index 8bb915d48dc7..7c8d53536920 100644 --- a/security/apparmor/include/path.h +++ b/security/apparmor/include/path.h @@ -22,8 +22,8 @@ enum path_flags { PATH_MEDIATE_DELETED = 0x20000, /* mediate deleted paths */ }; -int aa_path_name(const struct path *path, int flags, char *buffer, - const char **name, const char **info, +int aa_path_name(const struct path *path, int flags, bool profile_complain, + char *buffer, const char **name, const char **info, const char *disconnected); #define IN_ATOMIC true diff --git a/security/apparmor/mount.c b/security/apparmor/mount.c index 1e1878a2ed6b..21c933dc469c 100644 --- a/security/apparmor/mount.c +++ b/security/apparmor/mount.c @@ -323,7 +323,8 @@ static int match_mnt_path_str(const struct cred *subj_cred, if (!RULE_MEDIATES(rules, AA_CLASS_MOUNT)) return 0; - error = aa_path_name(mntpath, path_flags(profile, mntpath), buffer, + error = aa_path_name(mntpath, path_flags(profile, mntpath), + COMPLAIN_MODE(profile), buffer, &mntpnt, &info, profile->disconnected); if (error) goto audit; @@ -384,6 +385,7 @@ static int match_mnt(const struct cred *subj_cred, if (devpath) { error = aa_path_name(devpath, path_flags(profile, devpath), + COMPLAIN_MODE(profile), devbuffer, &devname, &info, profile->disconnected); if (error) @@ -618,7 +620,8 @@ static int profile_umount(const struct cred *subj_cred, if (!RULE_MEDIATES(rules, AA_CLASS_MOUNT)) return 0; - error = aa_path_name(path, path_flags(profile, path), buffer, &name, + error = aa_path_name(path, path_flags(profile, path), + COMPLAIN_MODE(profile), buffer, &name, &info, profile->disconnected); if (error) goto audit; @@ -686,11 +689,13 @@ static struct aa_label *build_pivotroot(const struct cred *subj_cred, return aa_get_newest_label(&profile->label); error = aa_path_name(old_path, path_flags(profile, old_path), + COMPLAIN_MODE(profile), old_buffer, &old_name, &info, profile->disconnected); if (error) goto audit; error = aa_path_name(new_path, path_flags(profile, new_path), + COMPLAIN_MODE(profile), new_buffer, &new_name, &info, profile->disconnected); if (error) diff --git a/security/apparmor/path.c b/security/apparmor/path.c index 45ec994b558d..800825caf7f4 100644 --- a/security/apparmor/path.c +++ b/security/apparmor/path.c @@ -46,19 +46,26 @@ static int prepend(char **buffer, int buflen, const char *str, int namelen) * namespace root. */ static int disconnect(const struct path *path, char *buf, char **name, - int flags, const char *disconnected) + int flags, bool profile_complain, + const char *disconnected) { int error = 0; if (!(flags & PATH_CONNECT_PATH) && !(((flags & CHROOT_NSCONNECT) == CHROOT_NSCONNECT) && our_mnt(path->mnt))) { - /* disconnected path, don't return pathname starting - * with '/' - */ error = -EACCES; - if (**name == '/') - *name = *name + 1; + if (profile_complain) { + // Replace the leading slash with a sentinel character + if (**name == '/') + **name = '#'; + } else { + /* disconnected path, don't return pathname starting + * with '/' + */ + if (**name == '/') + *name = *name + 1; + } } else { if (**name != '/') /* CONNECT_PATH with missing root */ @@ -86,7 +93,8 @@ static int disconnect(const struct path *path, char *buf, char **name, * a position in @buf */ static int d_namespace_path(const struct path *path, char *buf, char **name, - int flags, const char *disconnected) + int flags, bool profile_complain, + const char *disconnected) { char *res; int error = 0; @@ -110,7 +118,8 @@ static int d_namespace_path(const struct path *path, char *buf, char **name, error = prepend(name, *name - buf, "/proc", 5); goto out; } else - error = disconnect(path, buf, name, flags, + error = disconnect(path, buf, name, + flags, profile_complain, disconnected); goto out; } @@ -149,7 +158,8 @@ static int d_namespace_path(const struct path *path, char *buf, char **name, *name = res; if (!connected) - error = disconnect(path, buf, name, flags, disconnected); + error = disconnect(path, buf, name, flags, profile_complain, + disconnected); /* Handle two cases: * 1. A deleted dentry && profile is not allowing mediation of deleted @@ -178,6 +188,7 @@ static int d_namespace_path(const struct path *path, char *buf, char **name, * aa_path_name - get the pathname to a buffer ensure dir / is appended * @path: path the file (NOT NULL) * @flags: flags controlling path name generation + * @profile_complain: whether the lookup is done with a complain-mode profile * @buffer: buffer to put name in (NOT NULL) * @name: Returns - the generated path name if !error (NOT NULL) * @info: Returns - information on why the path lookup failed (MAYBE NULL) @@ -194,11 +205,13 @@ static int d_namespace_path(const struct path *path, char *buf, char **name, * * Returns: %0 else error code if could retrieve name */ -int aa_path_name(const struct path *path, int flags, char *buffer, - const char **name, const char **info, const char *disconnected) +int aa_path_name(const struct path *path, int flags, bool profile_complain, + char *buffer, const char **name, const char **info, + const char *disconnected) { char *str = NULL; - int error = d_namespace_path(path, buffer, &str, flags, disconnected); + int error = d_namespace_path(path, buffer, &str, + flags, profile_complain, disconnected); if (info && error) { if (error == -ENOENT) -- 2.43.0