diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index d24100f8fd98297b57c9917856df1a7c55051d5d..d1a6ce4997769bf7e26745e9e69ada679669b8ea 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -2086,6 +2086,11 @@ static struct aa_sfs_entry aa_sfs_entry_file[] = { { } }; +static struct aa_sfs_entry aa_sfs_entry_ptrace[] = { + AA_SFS_FILE_STRING("mask", "read trace"), + { } +}; + static struct aa_sfs_entry aa_sfs_entry_domain[] = { AA_SFS_FILE_BOOLEAN("change_hat", 1), AA_SFS_FILE_BOOLEAN("change_hatv", 1), @@ -2125,6 +2130,7 @@ static struct aa_sfs_entry aa_sfs_entry_features[] = { AA_SFS_FILE_U64("capability", VFS_CAP_FLAGS_MASK), AA_SFS_DIR("rlimit", aa_sfs_entry_rlimit), AA_SFS_DIR("caps", aa_sfs_entry_caps), + AA_SFS_DIR("ptrace", aa_sfs_entry_ptrace), AA_SFS_DIR("query", aa_sfs_entry_query), { } }; diff --git a/security/apparmor/include/ipc.h b/security/apparmor/include/ipc.h index fb3e751e6eed56929a0af674c2e698c065d8dbd2..656fdb81c8a0a08836b12ed614334222c0648212 100644 --- a/security/apparmor/include/ipc.h +++ b/security/apparmor/include/ipc.h @@ -21,6 +21,12 @@ struct aa_profile; #define AA_PTRACE_TRACE MAY_WRITE #define AA_PTRACE_READ MAY_READ +#define AA_MAY_BE_TRACED AA_MAY_APPEND +#define AA_MAY_BE_READ AA_MAY_CREATE +#define PTRACE_PERM_SHIFT 2 + +#define AA_PTRACE_PERM_MASK (AA_PTRACE_READ | AA_PTRACE_TRACE | \ + AA_MAY_BE_READ | AA_MAY_BE_TRACED) int aa_may_ptrace(struct aa_label *tracer, struct aa_label *tracee, u32 request); diff --git a/security/apparmor/ipc.c b/security/apparmor/ipc.c index f81649369f056da6fd2b79e55c806f2c1c0786db..11e66b5bbc4266c7c9b0f4fa47d13afdf709b47f 100644 --- a/security/apparmor/ipc.c +++ b/security/apparmor/ipc.c @@ -21,20 +21,76 @@ #include "include/policy.h" #include "include/ipc.h" +/** + * audit_ptrace_mask - convert mask to permission string + * @buffer: buffer to write string to (NOT NULL) + * @mask: permission mask to convert + */ +static void audit_ptrace_mask(struct audit_buffer *ab, u32 mask) +{ + switch (mask) { + case MAY_READ: + audit_log_string(ab, "read"); + break; + case MAY_WRITE: + audit_log_string(ab, "trace"); + break; + case AA_MAY_BE_READ: + audit_log_string(ab, "readby"); + break; + case AA_MAY_BE_TRACED: + audit_log_string(ab, "tracedby"); + break; + } +} + /* call back to audit ptrace fields */ static void audit_ptrace_cb(struct audit_buffer *ab, void *va) { struct common_audit_data *sa = va; + if (aad(sa)->request & AA_PTRACE_PERM_MASK) { + audit_log_format(ab, " requested_mask="); + audit_ptrace_mask(ab, aad(sa)->request); + + if (aad(sa)->denied & AA_PTRACE_PERM_MASK) { + audit_log_format(ab, " denied_mask="); + audit_ptrace_mask(ab, aad(sa)->denied); + } + } audit_log_format(ab, " peer="); aa_label_xaudit(ab, labels_ns(aad(sa)->label), aad(sa)->peer, FLAGS_NONE, GFP_ATOMIC); } +/* TODO: conditionals */ +static int profile_ptrace_perm(struct aa_profile *profile, + struct aa_profile *peer, u32 request, + struct common_audit_data *sa) +{ + struct aa_perms perms = { }; + + /* need because of peer in cross check */ + if (profile_unconfined(profile) || + !PROFILE_MEDIATES(profile, AA_CLASS_PTRACE)) + return 0; + + aad(sa)->peer = &peer->label; + aa_profile_match_label(profile, &peer->label, AA_CLASS_PTRACE, request, + &perms); + aa_apply_modes_to_perms(profile, &perms); + return aa_check_perms(profile, &perms, request, sa, audit_ptrace_cb); +} + static int cross_ptrace_perm(struct aa_profile *tracer, struct aa_profile *tracee, u32 request, struct common_audit_data *sa) { + if (PROFILE_MEDIATES(tracer, AA_CLASS_PTRACE)) + return xcheck(profile_ptrace_perm(tracer, tracee, request, sa), + profile_ptrace_perm(tracee, tracer, + request << PTRACE_PERM_SHIFT, + sa)); /* policy uses the old style capability check for ptrace */ if (profile_unconfined(tracer) || tracer == tracee) return 0;