mirror of
https://gitlab.com/apparmor/apparmor.git
synced 2025-03-04 00:14:44 +01:00
A fix-dfa.diff
- rework how null transitions are done. M fix-profile-namespaces.diff - fix namespaces to use the :namespace: syntax A cap-set.diff - allow a profile to set a tasks capabilities similar to fscap A rlimits.diff - allow control of a tasks rlimits
This commit is contained in:
parent
5f5aeee472
commit
6f65e6e8f5
5 changed files with 663 additions and 10 deletions
87
kernel-patches/for-mainline/cap-set.diff
Normal file
87
kernel-patches/for-mainline/cap-set.diff
Normal file
|
@ -0,0 +1,87 @@
|
|||
---
|
||||
security/apparmor/apparmor.h | 10 ++++++++++
|
||||
security/apparmor/apparmorfs.c | 4 ++--
|
||||
security/apparmor/lsm.c | 15 ++++++---------
|
||||
security/apparmor/module_interface.c | 2 ++
|
||||
4 files changed, 20 insertions(+), 11 deletions(-)
|
||||
|
||||
--- a/security/apparmor/apparmor.h
|
||||
+++ b/security/apparmor/apparmor.h
|
||||
@@ -158,8 +158,17 @@ struct aa_namespace {
|
||||
* @file_rules: dfa containing the profiles file rules
|
||||
* @flags: flags controlling profile behavior
|
||||
* @isstale: flag indicating if profile is stale
|
||||
+ * @set_caps: capabilities that are being set
|
||||
+ * @capabilities: capabilities mask
|
||||
+ * @audit_caps: caps that are to be audited
|
||||
+ * @quiet_caps: caps that should not be audited
|
||||
* @capabilities: capabilities granted by the process
|
||||
* @count: reference count of the profile
|
||||
+ * @task_contexts: list of tasks confined by profile
|
||||
+ * @lock: lock for the task_contexts list
|
||||
+ * @network_families: basic network permissions
|
||||
+ * @audit_network: which network permissions to force audit
|
||||
+ * @quiet_network: which network permissions to quiet rejects
|
||||
*
|
||||
* The AppArmor profile contains the basic confinement data. Each profile
|
||||
* has a name, and all nonstale profile are in a profile namespace.
|
||||
@@ -183,6 +192,7 @@ struct aa_profile {
|
||||
} flags;
|
||||
int isstale;
|
||||
|
||||
+ kernel_cap_t set_caps;
|
||||
kernel_cap_t capabilities;
|
||||
kernel_cap_t audit_caps;
|
||||
kernel_cap_t quiet_caps;
|
||||
--- a/security/apparmor/apparmorfs.c
|
||||
+++ b/security/apparmor/apparmorfs.c
|
||||
@@ -103,8 +103,8 @@ static struct file_operations apparmorfs
|
||||
static ssize_t aa_features_read(struct file *file, char __user *buf,
|
||||
size_t size, loff_t *ppos)
|
||||
{
|
||||
- const char *features = "file=3.0 capability=1.0 network=1.0 "
|
||||
- "change_hat=1.3 change_profile=1.0 "
|
||||
+ const char *features = "file=3.0 capability=2.0 network=1.0 "
|
||||
+ "change_hat=1.4 change_profile=1.0 "
|
||||
"aanamespaces=1.0";
|
||||
|
||||
return simple_read_from_buffer(buf, size, ppos, features,
|
||||
--- a/security/apparmor/lsm.c
|
||||
+++ b/security/apparmor/lsm.c
|
||||
@@ -210,19 +210,16 @@ static int apparmor_ptrace(struct task_s
|
||||
static int apparmor_capable(struct task_struct *task, int cap)
|
||||
{
|
||||
int error;
|
||||
+ struct aa_task_context *cxt;
|
||||
|
||||
/* cap_capable returns 0 on success, else -EPERM */
|
||||
error = cap_capable(task, cap);
|
||||
|
||||
- if (!error) {
|
||||
- struct aa_task_context *cxt;
|
||||
-
|
||||
- rcu_read_lock();
|
||||
- cxt = aa_task_context(task);
|
||||
- if (cxt)
|
||||
- error = aa_capability(cxt, cap);
|
||||
- rcu_read_unlock();
|
||||
- }
|
||||
+ rcu_read_lock();
|
||||
+ cxt = aa_task_context(task);
|
||||
+ if (cxt && (!error || cap_raised(cxt->profile->set_caps, cap)))
|
||||
+ error = aa_capability(cxt, cap);
|
||||
+ rcu_read_unlock();
|
||||
|
||||
return error;
|
||||
}
|
||||
--- a/security/apparmor/module_interface.c
|
||||
+++ b/security/apparmor/module_interface.c
|
||||
@@ -316,6 +316,8 @@ static struct aa_profile *aa_unpack_prof
|
||||
goto fail;
|
||||
if (!aa_is_u32(e, &(profile->quiet_caps), NULL))
|
||||
goto fail;
|
||||
+ if (!aa_is_u32(e, &(profile->set_caps), NULL))
|
||||
+ goto fail;
|
||||
|
||||
size = aa_is_array(e, "net_allowed_af");
|
||||
if (size) {
|
134
kernel-patches/for-mainline/fix-dfa.diff
Normal file
134
kernel-patches/for-mainline/fix-dfa.diff
Normal file
|
@ -0,0 +1,134 @@
|
|||
---
|
||||
security/apparmor/match.c | 74 +++++++++++++++++++++++++++++++---------------
|
||||
1 file changed, 51 insertions(+), 23 deletions(-)
|
||||
|
||||
--- a/security/apparmor/match.c
|
||||
+++ b/security/apparmor/match.c
|
||||
@@ -213,17 +213,23 @@ void aa_match_free(struct aa_dfa *dfa)
|
||||
}
|
||||
|
||||
/**
|
||||
- * aa_dfa_next_state - traverse @dfa to find state @str stops at
|
||||
+ * aa_dfa_next_state_len - 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
|
||||
+ * @str: the string of bytes to match against the dfa
|
||||
+ * @len: length of the string of bytes to match
|
||||
*
|
||||
* 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.
|
||||
+ *
|
||||
+ * aa_dfa_next_state could be implement using this function by doing
|
||||
+ * return aa_dfa_next_state_len(dfa, start, str, strlen(str));
|
||||
+ * but that would require traversing the string twice and be slightly
|
||||
+ * slower.
|
||||
*/
|
||||
-unsigned int aa_dfa_next_state(struct aa_dfa *dfa, unsigned int start,
|
||||
- const char *str)
|
||||
+unsigned int aa_dfa_next_state_len(struct aa_dfa *dfa, unsigned int start,
|
||||
+ const char *str, int len)
|
||||
{
|
||||
u16 *def = DEFAULT_TABLE(dfa);
|
||||
u32 *base = BASE_TABLE(dfa);
|
||||
@@ -237,7 +243,7 @@ unsigned int aa_dfa_next_state(struct aa
|
||||
/* current state is <state>, matching character *str */
|
||||
if (dfa->tables[YYTD_ID_EC - 1]) {
|
||||
u8 *equiv = EQUIV_TABLE(dfa);
|
||||
- while (*str) {
|
||||
+ for (; len; len--) {
|
||||
pos = base[state] + equiv[(u8)*str++];
|
||||
if (check[pos] == state)
|
||||
state = next[pos];
|
||||
@@ -245,7 +251,7 @@ unsigned int aa_dfa_next_state(struct aa
|
||||
state = def[state];
|
||||
}
|
||||
} else {
|
||||
- while (*str) {
|
||||
+ for (; len; len--) {
|
||||
pos = base[state] + (u8)*str++;
|
||||
if (check[pos] == state)
|
||||
state = next[pos];
|
||||
@@ -257,15 +263,17 @@ unsigned int aa_dfa_next_state(struct aa
|
||||
}
|
||||
|
||||
/**
|
||||
- * aa_dfa_null_transition - step to next state after null character
|
||||
- * @dfa: the dfa to match against
|
||||
+ * 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 null terminated string of bytes to match against the dfa
|
||||
*
|
||||
- * 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.
|
||||
+ * 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_null_transition(struct aa_dfa *dfa, unsigned int start)
|
||||
+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);
|
||||
@@ -273,26 +281,46 @@ unsigned int aa_dfa_null_transition(stru
|
||||
u16 *check = CHECK_TABLE(dfa);
|
||||
unsigned int state = start, pos;
|
||||
|
||||
+ if (state == 0)
|
||||
+ return 0;
|
||||
+
|
||||
/* current state is <state>, 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];
|
||||
+ while (*str) {
|
||||
+ pos = base[state] + equiv[(u8)*str++];
|
||||
+ 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];
|
||||
+ while (*str) {
|
||||
+ pos = base[state] + (u8)*str++;
|
||||
+ if (check[pos] == state)
|
||||
+ state = next[pos];
|
||||
+ else
|
||||
+ state = def[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)
|
||||
+{
|
||||
+ return aa_dfa_next_state_len(dfa, start, "", 1);
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
* 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
|
|
@ -1,8 +1,8 @@
|
|||
---
|
||||
security/apparmor/list.c | 2 +-
|
||||
security/apparmor/main.c | 19 +++++++++++--------
|
||||
security/apparmor/main.c | 20 +++++++++++---------
|
||||
security/apparmor/procattr.c | 13 +++++++------
|
||||
3 files changed, 19 insertions(+), 15 deletions(-)
|
||||
3 files changed, 19 insertions(+), 16 deletions(-)
|
||||
|
||||
--- a/security/apparmor/list.c
|
||||
+++ b/security/apparmor/list.c
|
||||
|
@ -30,7 +30,7 @@
|
|||
}
|
||||
|
||||
audit_log_end(ab);
|
||||
@@ -1364,15 +1366,16 @@ repeat:
|
||||
@@ -1364,15 +1366,15 @@ repeat:
|
||||
|
||||
if (hat_name) {
|
||||
char *name, *profile_name;
|
||||
|
@ -39,17 +39,17 @@
|
|||
- become more generic then this will be needed.
|
||||
- if (!(aa_match(profile->file_rules, hat_name) &
|
||||
- AA_CHANGE_PROFILE)) {
|
||||
+/*
|
||||
+ int foo = aa_match(profile->file_rules, hat_name, NULL);
|
||||
- error = -EACCES;
|
||||
+ if (!PROFILE_COMPLAIN(profile) &&
|
||||
+ !(aa_match(profile->file_rules, hat_name, NULL) &
|
||||
+ AA_CHANGE_HAT)) {
|
||||
+printk("failed to change_hat %s 0x%x\n", hat_name, foo);
|
||||
error = -EACCES;
|
||||
+ !(aa_match(profile->file_rules, hat_name, NULL)
|
||||
+ & AA_CHANGE_HAT)) {
|
||||
+ /* missing permission to change_hat is treated the
|
||||
+ * same as a failed hat search */
|
||||
+ error = -ENOENT;
|
||||
goto out;
|
||||
}
|
||||
- */
|
||||
+*/
|
||||
+
|
||||
if (previous_profile)
|
||||
profile_name = previous_profile->name;
|
||||
else
|
||||
|
|
429
kernel-patches/for-mainline/rlimits.diff
Normal file
429
kernel-patches/for-mainline/rlimits.diff
Normal file
|
@ -0,0 +1,429 @@
|
|||
---
|
||||
security/apparmor/apparmor.h | 23 ++++++
|
||||
security/apparmor/lsm.c | 16 ++++
|
||||
security/apparmor/main.c | 132 +++++++++++++++++++++++++++++++----
|
||||
security/apparmor/module_interface.c | 56 ++++++++++++++
|
||||
4 files changed, 214 insertions(+), 13 deletions(-)
|
||||
|
||||
--- a/security/apparmor/apparmor.h
|
||||
+++ b/security/apparmor/apparmor.h
|
||||
@@ -16,6 +16,7 @@
|
||||
#include <linux/fs.h>
|
||||
#include <linux/binfmts.h>
|
||||
#include <linux/rcupdate.h>
|
||||
+#include <linux/resource.h>
|
||||
#include <linux/socket.h>
|
||||
#include <net/sock.h>
|
||||
|
||||
@@ -129,6 +130,18 @@ extern unsigned int apparmor_path_max;
|
||||
|
||||
#define AA_ERROR(fmt, args...) printk(KERN_ERR "AppArmor: " fmt, ##args)
|
||||
|
||||
+/* struct aa_rlimit - rlimits settings for the profile
|
||||
+ * @mask: which hard limits to set
|
||||
+ * @limits: rlimit values that override task limits
|
||||
+ *
|
||||
+ * AppArmor rlimits are used to set confined task rlimits. Only the
|
||||
+ * limits specified in @mask will be controlled by apparmor.
|
||||
+ */
|
||||
+struct aa_rlimit {
|
||||
+ unsigned int mask;
|
||||
+ struct rlimit limits[RLIM_NLIMITS];
|
||||
+};
|
||||
+
|
||||
struct aa_profile;
|
||||
|
||||
/* struct aa_namespace - namespace for a set of profiles
|
||||
@@ -163,6 +176,8 @@ struct aa_namespace {
|
||||
* @audit_caps: caps that are to be audited
|
||||
* @quiet_caps: caps that should not be audited
|
||||
* @capabilities: capabilities granted by the process
|
||||
+ * @rlimits: rlimits for the profile
|
||||
+ * @task_count: how many tasks the profile is attached to
|
||||
* @count: reference count of the profile
|
||||
* @task_contexts: list of tasks confined by profile
|
||||
* @lock: lock for the task_contexts list
|
||||
@@ -197,6 +212,9 @@ struct aa_profile {
|
||||
kernel_cap_t audit_caps;
|
||||
kernel_cap_t quiet_caps;
|
||||
|
||||
+ struct aa_rlimit rlimits;
|
||||
+ unsigned int task_count;
|
||||
+
|
||||
struct kref count;
|
||||
struct list_head task_contexts;
|
||||
spinlock_t lock;
|
||||
@@ -248,6 +266,7 @@ struct aa_audit {
|
||||
const char *name2;
|
||||
const char *name3;
|
||||
int request_mask, denied_mask, audit_mask;
|
||||
+ int rlimit;
|
||||
struct iattr *iattr;
|
||||
pid_t task, parent;
|
||||
int family, type, protocol;
|
||||
@@ -315,6 +334,10 @@ extern int aa_may_ptrace(struct aa_task_
|
||||
extern int aa_net_perm(struct aa_profile *profile, char *operation,
|
||||
int family, int type, int protocol);
|
||||
extern int aa_revalidate_sk(struct sock *sk, char *operation);
|
||||
+extern int aa_task_setrlimit(struct aa_profile *profile, unsigned int resource,
|
||||
+ struct rlimit *new_rlim);
|
||||
+extern void aa_set_rlimits(struct task_struct *task, struct aa_profile *profile);
|
||||
+
|
||||
|
||||
/* lsm.c */
|
||||
extern int apparmor_initialized;
|
||||
--- a/security/apparmor/lsm.c
|
||||
+++ b/security/apparmor/lsm.c
|
||||
@@ -856,6 +856,21 @@ static int apparmor_setprocattr(struct t
|
||||
return error;
|
||||
}
|
||||
|
||||
+static int apparmor_task_setrlimit(unsigned int resource,
|
||||
+ struct rlimit *new_rlim)
|
||||
+{
|
||||
+ struct aa_profile *profile;
|
||||
+ int error = 0;
|
||||
+
|
||||
+ profile = aa_get_profile(current);
|
||||
+ if (profile) {
|
||||
+ error = aa_task_setrlimit(profile, resource, new_rlim);
|
||||
+ }
|
||||
+ aa_put_profile(profile);
|
||||
+
|
||||
+ return error;
|
||||
+}
|
||||
+
|
||||
struct security_operations apparmor_ops = {
|
||||
.ptrace = apparmor_ptrace,
|
||||
.capget = cap_capget,
|
||||
@@ -897,6 +912,7 @@ struct security_operations apparmor_ops
|
||||
.task_free_security = apparmor_task_free_security,
|
||||
.task_post_setuid = cap_task_post_setuid,
|
||||
.task_reparent_to_init = cap_task_reparent_to_init,
|
||||
+ .task_setrlimit = apparmor_task_setrlimit,
|
||||
|
||||
.getprocattr = apparmor_getprocattr,
|
||||
.setprocattr = apparmor_setprocattr,
|
||||
--- a/security/apparmor/main.c
|
||||
+++ b/security/apparmor/main.c
|
||||
@@ -196,6 +196,9 @@ static int aa_audit_base(struct aa_profi
|
||||
if (sa->request_mask)
|
||||
audit_log_format(ab, " fsuid=%d", current->fsuid);
|
||||
|
||||
+ if (sa->rlimit)
|
||||
+ audit_log_format(ab, " rlimit=%d", sa->rlimit - 1);
|
||||
+
|
||||
if (sa->iattr) {
|
||||
struct iattr *iattr = sa->iattr;
|
||||
|
||||
@@ -873,6 +876,78 @@ int aa_revalidate_sk(struct sock *sk, ch
|
||||
|
||||
return error;
|
||||
}
|
||||
+/**
|
||||
+ * aa_task_setrlimit - test permission to set an rlimit
|
||||
+ * @profile - profile confining the task
|
||||
+ * @resource - the resource being set
|
||||
+ * @new_rlim - the new resource limit
|
||||
+ *
|
||||
+ * Control raising the processes hard limit.
|
||||
+ */
|
||||
+int aa_task_setrlimit(struct aa_profile *profile, unsigned int resource,
|
||||
+ struct rlimit *new_rlim)
|
||||
+{
|
||||
+ struct aa_audit sa;
|
||||
+ int error = 0;
|
||||
+
|
||||
+ memset(&sa, 0, sizeof(sa));
|
||||
+ sa.operation = "setrlimit";
|
||||
+ sa.gfp_mask = GFP_KERNEL;
|
||||
+ sa.rlimit = resource + 1;
|
||||
+
|
||||
+ if (profile->rlimits.mask & (1 << resource) &&
|
||||
+ new_rlim->rlim_max > profile->rlimits.limits[resource].rlim_max)
|
||||
+ sa.error_code = -EACCES;
|
||||
+
|
||||
+ error = aa_audit(profile, &sa);
|
||||
+
|
||||
+ return error;
|
||||
+}
|
||||
+
|
||||
+static int aa_rlimit_nproc(struct aa_profile *profile) {
|
||||
+ if ((profile->rlimits.mask & (1 << RLIMIT_NPROC)) &&
|
||||
+ profile->task_count >= profile->rlimits.limits[RLIMIT_NPROC].rlim_max)
|
||||
+ return -EAGAIN;
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+void aa_set_rlimits(struct task_struct *task, struct aa_profile *profile)
|
||||
+{
|
||||
+ int i, mask;
|
||||
+
|
||||
+ if (!profile)
|
||||
+ return;
|
||||
+
|
||||
+ if (!profile->rlimits.mask)
|
||||
+ return;
|
||||
+
|
||||
+ task_lock(task->group_leader);
|
||||
+ mask = 1;
|
||||
+ for (i = 0; i < RLIM_NLIMITS; i++, mask <<= 1) {
|
||||
+ struct rlimit new_rlim, *old_rlim;
|
||||
+
|
||||
+ /* check to see if NPROC which is per profile and handled
|
||||
+ * in clone/exec or whether this is a limit to be set
|
||||
+ * can't set cpu limit either right now
|
||||
+ */
|
||||
+ if (i == RLIMIT_NPROC || i == RLIMIT_CPU)
|
||||
+ continue;
|
||||
+
|
||||
+ old_rlim = task->signal->rlim + i;
|
||||
+ new_rlim = *old_rlim;
|
||||
+
|
||||
+ if (mask & profile->rlimits.mask &&
|
||||
+ profile->rlimits.limits[i].rlim_max < new_rlim.rlim_max) {
|
||||
+ new_rlim.rlim_max = profile->rlimits.limits[i].rlim_max;
|
||||
+ /* soft limit should not exceed hard limit */
|
||||
+ if (new_rlim.rlim_cur > new_rlim.rlim_max)
|
||||
+ new_rlim.rlim_cur = new_rlim.rlim_max;
|
||||
+ }
|
||||
+
|
||||
+ *old_rlim = new_rlim;
|
||||
+ }
|
||||
+ task_unlock(task->group_leader);
|
||||
+}
|
||||
|
||||
/*******************************
|
||||
* Global task related functions
|
||||
@@ -886,6 +961,7 @@ int aa_revalidate_sk(struct sock *sk, ch
|
||||
*/
|
||||
int aa_clone(struct task_struct *child)
|
||||
{
|
||||
+ struct aa_audit sa;
|
||||
struct aa_task_context *cxt, *child_cxt;
|
||||
struct aa_profile *profile;
|
||||
|
||||
@@ -895,6 +971,11 @@ int aa_clone(struct task_struct *child)
|
||||
if (!child_cxt)
|
||||
return -ENOMEM;
|
||||
|
||||
+ memset(&sa, 0, sizeof(sa));
|
||||
+ sa.operation = "clone";
|
||||
+ sa.task = child->pid;
|
||||
+ sa.gfp_mask = GFP_KERNEL;
|
||||
+
|
||||
repeat:
|
||||
profile = aa_get_profile(current);
|
||||
if (profile) {
|
||||
@@ -911,18 +992,23 @@ repeat:
|
||||
goto repeat;
|
||||
}
|
||||
|
||||
+ sa.error_code = aa_rlimit_nproc(profile);
|
||||
+ if (sa.error_code) {
|
||||
+ sa.info = "rlimit nproc limit exceeded";
|
||||
+ unlock_profile(profile);
|
||||
+ aa_audit_reject(profile, &sa);
|
||||
+ aa_put_profile(profile);
|
||||
+ return sa.error_code;
|
||||
+ }
|
||||
+
|
||||
/* No need to grab the child's task lock here. */
|
||||
aa_change_task_context(child, child_cxt, profile,
|
||||
cxt->cookie, cxt->previous_profile);
|
||||
+
|
||||
unlock_profile(profile);
|
||||
|
||||
if (APPARMOR_COMPLAIN(child_cxt) &&
|
||||
profile == profile->ns->null_complain_profile) {
|
||||
- struct aa_audit sa;
|
||||
- memset(&sa, 0, sizeof(sa));
|
||||
- sa.operation = "clone";
|
||||
- sa.gfp_mask = GFP_KERNEL;
|
||||
- sa.task = child->pid;
|
||||
aa_audit_hint(profile, &sa);
|
||||
}
|
||||
aa_put_profile(profile);
|
||||
@@ -1100,6 +1186,10 @@ repeat:
|
||||
sa.task = current->parent->pid;
|
||||
aa_audit_reject(profile, &sa);
|
||||
}
|
||||
+ if (PTR_ERR(old_profile) == -EAGAIN) {
|
||||
+ sa.info = "rlimit nproc limit exceeded";
|
||||
+ aa_audit_reject(profile, &sa);
|
||||
+ }
|
||||
new_profile = old_profile;
|
||||
goto cleanup;
|
||||
}
|
||||
@@ -1239,6 +1329,12 @@ static int do_change_profile(struct aa_p
|
||||
goto out;
|
||||
}
|
||||
|
||||
+ if ((error = aa_rlimit_nproc(new_profile))) {
|
||||
+ sa->info = "rlimit nproc limit exceeded";
|
||||
+ aa_audit_reject(cxt->profile, sa);
|
||||
+ goto out;
|
||||
+ }
|
||||
+
|
||||
if (new_profile == ns->null_complain_profile)
|
||||
aa_audit_hint(cxt->profile, sa);
|
||||
|
||||
@@ -1427,17 +1523,18 @@ struct aa_profile *__aa_replace_profile(
|
||||
|
||||
cxt = lock_task_and_profiles(task, profile);
|
||||
if (unlikely(profile && profile->isstale)) {
|
||||
- task_unlock(task);
|
||||
- unlock_both_profiles(profile, cxt ? cxt->profile : NULL);
|
||||
- aa_free_task_context(new_cxt);
|
||||
- return ERR_PTR(-ESTALE);
|
||||
+ old_profile = ERR_PTR(-ESTALE);
|
||||
+ goto error;
|
||||
}
|
||||
|
||||
if ((current->ptrace & PT_PTRACED) && aa_may_ptrace(cxt, profile)) {
|
||||
- task_unlock(task);
|
||||
- unlock_both_profiles(profile, cxt ? cxt->profile : NULL);
|
||||
- aa_free_task_context(new_cxt);
|
||||
- return ERR_PTR(-EPERM);
|
||||
+ old_profile = ERR_PTR(-EPERM);
|
||||
+ goto error;
|
||||
+ }
|
||||
+
|
||||
+ if (aa_rlimit_nproc(profile)) {
|
||||
+ old_profile = ERR_PTR(-EAGAIN);
|
||||
+ goto error;
|
||||
}
|
||||
|
||||
if (cxt)
|
||||
@@ -1445,8 +1542,15 @@ struct aa_profile *__aa_replace_profile(
|
||||
aa_change_task_context(task, new_cxt, profile, 0, NULL);
|
||||
|
||||
task_unlock(task);
|
||||
+ aa_set_rlimits(task, profile);
|
||||
unlock_both_profiles(profile, old_profile);
|
||||
return old_profile;
|
||||
+
|
||||
+error:
|
||||
+ task_unlock(task);
|
||||
+ unlock_both_profiles(profile, cxt ? cxt->profile : NULL);
|
||||
+ aa_free_task_context(new_cxt);
|
||||
+ return old_profile;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1511,6 +1615,7 @@ void aa_change_task_context(struct task_
|
||||
|
||||
if (old_cxt) {
|
||||
list_del_init(&old_cxt->list);
|
||||
+ old_cxt->profile->task_count--;
|
||||
call_rcu(&old_cxt->rcu, free_aa_task_context_rcu_callback);
|
||||
}
|
||||
if (new_cxt) {
|
||||
@@ -1522,6 +1627,7 @@ void aa_change_task_context(struct task_
|
||||
new_cxt->cookie = cookie;
|
||||
new_cxt->task = task;
|
||||
new_cxt->profile = aa_dup_profile(profile);
|
||||
+ profile->task_count++;
|
||||
new_cxt->previous_profile = aa_dup_profile(previous_profile);
|
||||
list_move(&new_cxt->list, &profile->task_contexts);
|
||||
}
|
||||
--- a/security/apparmor/module_interface.c
|
||||
+++ b/security/apparmor/module_interface.c
|
||||
@@ -177,6 +177,22 @@ fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
+static int aa_is_u64(struct aa_ext *e, u64 *data, const char *name)
|
||||
+{
|
||||
+ void *pos = e->pos;
|
||||
+ if (aa_is_nameX(e, AA_U64, name)) {
|
||||
+ if (!aa_inbounds(e, sizeof(u64)))
|
||||
+ goto fail;
|
||||
+ if (data)
|
||||
+ *data = le64_to_cpu(get_unaligned((u64 *)e->pos));
|
||||
+ e->pos += sizeof(u64);
|
||||
+ return 1;
|
||||
+ }
|
||||
+fail:
|
||||
+ e->pos = pos;
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
static size_t aa_is_array(struct aa_ext *e, const char *name)
|
||||
{
|
||||
void *pos = e->pos;
|
||||
@@ -273,6 +289,39 @@ struct aa_dfa *aa_unpack_dfa(struct aa_e
|
||||
return dfa;
|
||||
}
|
||||
|
||||
+int aa_unpack_rlimits(struct aa_ext *e, struct aa_profile *profile)
|
||||
+{
|
||||
+ void *pos = e->pos;
|
||||
+
|
||||
+ /* rlimits are optional */
|
||||
+ if (aa_is_nameX(e, AA_STRUCT, "rlimits")) {
|
||||
+ int i, size;
|
||||
+ u32 tmp;
|
||||
+ if (!aa_is_u32(e, &tmp, NULL))
|
||||
+ goto fail;
|
||||
+ profile->rlimits.mask = tmp;
|
||||
+
|
||||
+ size = aa_is_array(e, NULL);
|
||||
+ if (size != RLIM_NLIMITS)
|
||||
+ goto fail;
|
||||
+ for (i = 0; i < size; i++) {
|
||||
+ u64 tmp;
|
||||
+ if (!aa_is_u64(e, &tmp, NULL))
|
||||
+ goto fail;
|
||||
+ profile->rlimits.limits[i].rlim_max = tmp;
|
||||
+ }
|
||||
+ if (!aa_is_nameX(e, AA_ARRAYEND, NULL))
|
||||
+ goto fail;
|
||||
+ if (!aa_is_nameX(e, AA_STRUCTEND, NULL))
|
||||
+ goto fail;
|
||||
+ }
|
||||
+ return 1;
|
||||
+
|
||||
+fail:
|
||||
+ e->pos = pos;
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
/**
|
||||
* aa_unpack_profile - unpack a serialized profile
|
||||
* @e: serialized data extent information
|
||||
@@ -319,6 +368,9 @@ static struct aa_profile *aa_unpack_prof
|
||||
if (!aa_is_u32(e, &(profile->set_caps), NULL))
|
||||
goto fail;
|
||||
|
||||
+ if (!aa_unpack_rlimits(e, profile))
|
||||
+ goto fail;
|
||||
+
|
||||
size = aa_is_array(e, "net_allowed_af");
|
||||
if (size) {
|
||||
if (size > AF_MAX)
|
||||
@@ -565,6 +617,8 @@ ssize_t aa_replace_profile(void *udata,
|
||||
if (!old_profile)
|
||||
goto out;
|
||||
|
||||
+ /* do not fail replacement based off of profile's NPROC rlimit */
|
||||
+
|
||||
/*
|
||||
* Replacement needs to allocate a new aa_task_context for each
|
||||
* task confined by old_profile. To do this the profile locks
|
||||
@@ -585,6 +639,7 @@ ssize_t aa_replace_profile(void *udata,
|
||||
task_lock(task);
|
||||
task_replace(task, new_cxt, new_profile);
|
||||
task_unlock(task);
|
||||
+ aa_set_rlimits(task, new_profile);
|
||||
new_cxt = NULL;
|
||||
}
|
||||
unlock_both_profiles(old_profile, new_profile);
|
||||
@@ -604,6 +659,7 @@ out:
|
||||
*
|
||||
* remove a profile from the profile list and all aa_task_context references
|
||||
* to said profile.
|
||||
+ * NOTE: removing confinement does not restore rlimits to preconfinemnet values
|
||||
*/
|
||||
ssize_t aa_remove_profile(char *name, size_t size)
|
||||
{
|
|
@ -107,3 +107,6 @@ audit-log-type-in-syslog.diff
|
|||
#FS2.1.3_fix-unionfs-with-AppArmor.patch
|
||||
|
||||
fix-profile-namespaces.diff
|
||||
fix-dfa.diff
|
||||
cap-set.diff
|
||||
rlimits.diff
|
||||
|
|
Loading…
Add table
Reference in a new issue