+++ /dev/null
-From: John Johansen <jjohansen@suse.de>
-Subject: AppArmor: per profile controls for system rlimits
-
-Provide contol of rlimits on a per profile basis. Each profile provides
-a per limit contol and corresponding hard limit value, such that when a
-profile becomes attached to a task it sets the tasks limits to be <= to
-the profiles specified limits. Note: the profile limit value will not
-raise a tasks limit if it is already less than the profile mandates.
-
-In addition to setting a tasks limits, the ability to set limits on
-a confined task are controlled. AppArmor only controls the raising
-of a tasks limits Tasks with CAP_SYS_RESOURCE can have their hard limits
-raised up to the value specified by the profile. AppArmor does not
-prevent a task for lowering its hard limits, nor does it provide
-additional control on soft limits.
-
-AppArmor only controls the limits specified in a profile so that
-any limit not specified is free to be modified subject to standard
-linux limitations.
-
----
- security/apparmor/apparmor.h | 23 ++++++
- security/apparmor/apparmorfs.c | 2
- security/apparmor/lsm.c | 16 ++++
- security/apparmor/main.c | 132 +++++++++++++++++++++++++++++++----
- security/apparmor/module_interface.c | 56 ++++++++++++++
- 5 files changed, 215 insertions(+), 14 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>
-
-@@ -139,6 +140,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
-@@ -173,6 +186,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
-@@ -210,6 +225,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;
-@@ -261,6 +279,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;
-@@ -328,6 +347,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/apparmorfs.c
-+++ b/security/apparmor/apparmorfs.c
-@@ -106,7 +106,7 @@ static ssize_t aa_features_read(struct f
- {
- const char *features = "file=3.0 capability=2.0 network=1.0 "
- "change_hat=1.5 change_profile=1.0 "
-- "aanamespaces=1.0";
-+ "aanamespaces=1.0 rlimit=1.0";
-
- return simple_read_from_buffer(buf, size, ppos, features,
- strlen(features));
---- a/security/apparmor/lsm.c
-+++ b/security/apparmor/lsm.c
-@@ -883,6 +883,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,
-@@ -926,6 +941,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
-@@ -177,6 +177,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;
-
-@@ -872,6 +875,79 @@ 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 && (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
-@@ -885,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;
-
-@@ -894,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) {
-@@ -910,18 +992,22 @@ repeat:
- goto repeat;
- }
-
-+ if (aa_rlimit_nproc(profile)) {
-+ sa.info = "rlimit nproc limit exceeded";
-+ unlock_profile(profile);
-+ aa_audit_reject(profile, &sa);
-+ aa_put_profile(profile);
-+ return -EAGAIN;
-+ }
-+
- /* 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);
-@@ -1156,6 +1242,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;
- }
-@@ -1303,6 +1393,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);
-
-@@ -1481,17 +1577,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)
-@@ -1499,8 +1596,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;
- }
-
- /**
-@@ -1565,6 +1669,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) {
-@@ -1576,6 +1681,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;
-@@ -312,6 +328,39 @@ fail:
- return 0;
- }
-
-+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 = 0;
-+ 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 = 0;
-+ 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
-@@ -355,6 +404,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)
-@@ -614,6 +666,8 @@ ssize_t aa_replace_profile(void *udata,
- sa.operation = "profile_load";
- 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
-@@ -634,6 +688,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);
-@@ -656,6 +711,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)
- {