1 From: John Johansen <jjohansen@suse.de>
2 Subject: AppArmor: per profile controls for system rlimits
4 Provide contol of rlimits on a per profile basis. Each profile provides
5 a per limit contol and corresponding hard limit value, such that when a
6 profile becomes attached to a task it sets the tasks limits to be <= to
7 the profiles specified limits. Note: the profile limit value will not
8 raise a tasks limit if it is already less than the profile mandates.
10 In addition to setting a tasks limits, the ability to set limits on
11 a confined task are controlled. AppArmor only controls the raising
12 of a tasks limits Tasks with CAP_SYS_RESOURCE can have their hard limits
13 raised up to the value specified by the profile. AppArmor does not
14 prevent a task for lowering its hard limits, nor does it provide
15 additional control on soft limits.
17 AppArmor only controls the limits specified in a profile so that
18 any limit not specified is free to be modified subject to standard
22 security/apparmor/apparmor.h | 23 ++++++
23 security/apparmor/apparmorfs.c | 2
24 security/apparmor/lsm.c | 16 ++++
25 security/apparmor/main.c | 132 +++++++++++++++++++++++++++++++----
26 security/apparmor/module_interface.c | 56 ++++++++++++++
27 5 files changed, 215 insertions(+), 14 deletions(-)
29 --- a/security/apparmor/apparmor.h
30 +++ b/security/apparmor/apparmor.h
33 #include <linux/binfmts.h>
34 #include <linux/rcupdate.h>
35 +#include <linux/resource.h>
36 #include <linux/socket.h>
39 @@ -139,6 +140,18 @@ extern unsigned int apparmor_path_max;
41 #define AA_ERROR(fmt, args...) printk(KERN_ERR "AppArmor: " fmt, ##args)
43 +/* struct aa_rlimit - rlimits settings for the profile
44 + * @mask: which hard limits to set
45 + * @limits: rlimit values that override task limits
47 + * AppArmor rlimits are used to set confined task rlimits. Only the
48 + * limits specified in @mask will be controlled by apparmor.
52 + struct rlimit limits[RLIM_NLIMITS];
57 /* struct aa_namespace - namespace for a set of profiles
58 @@ -173,6 +186,8 @@ struct aa_namespace {
59 * @audit_caps: caps that are to be audited
60 * @quiet_caps: caps that should not be audited
61 * @capabilities: capabilities granted by the process
62 + * @rlimits: rlimits for the profile
63 + * @task_count: how many tasks the profile is attached to
64 * @count: reference count of the profile
65 * @task_contexts: list of tasks confined by profile
66 * @lock: lock for the task_contexts list
67 @@ -210,6 +225,9 @@ struct aa_profile {
68 kernel_cap_t audit_caps;
69 kernel_cap_t quiet_caps;
71 + struct aa_rlimit rlimits;
72 + unsigned int task_count;
75 struct list_head task_contexts;
77 @@ -261,6 +279,7 @@ struct aa_audit {
80 int request_mask, denied_mask, audit_mask;
84 int family, type, protocol;
85 @@ -328,6 +347,10 @@ extern int aa_may_ptrace(struct aa_task_
86 extern int aa_net_perm(struct aa_profile *profile, char *operation,
87 int family, int type, int protocol);
88 extern int aa_revalidate_sk(struct sock *sk, char *operation);
89 +extern int aa_task_setrlimit(struct aa_profile *profile, unsigned int resource,
90 + struct rlimit *new_rlim);
91 +extern void aa_set_rlimits(struct task_struct *task, struct aa_profile *profile);
95 extern int apparmor_initialized;
96 --- a/security/apparmor/apparmorfs.c
97 +++ b/security/apparmor/apparmorfs.c
98 @@ -106,7 +106,7 @@ static ssize_t aa_features_read(struct f
100 const char *features = "file=3.0 capability=2.0 network=1.0 "
101 "change_hat=1.5 change_profile=1.0 "
102 - "aanamespaces=1.0";
103 + "aanamespaces=1.0 rlimit=1.0";
105 return simple_read_from_buffer(buf, size, ppos, features,
107 --- a/security/apparmor/lsm.c
108 +++ b/security/apparmor/lsm.c
109 @@ -883,6 +883,21 @@ static int apparmor_setprocattr(struct t
113 +static int apparmor_task_setrlimit(unsigned int resource,
114 + struct rlimit *new_rlim)
116 + struct aa_profile *profile;
119 + profile = aa_get_profile(current);
121 + error = aa_task_setrlimit(profile, resource, new_rlim);
123 + aa_put_profile(profile);
128 struct security_operations apparmor_ops = {
129 .ptrace = apparmor_ptrace,
130 .capget = cap_capget,
131 @@ -926,6 +941,7 @@ struct security_operations apparmor_ops
132 .task_free_security = apparmor_task_free_security,
133 .task_post_setuid = cap_task_post_setuid,
134 .task_reparent_to_init = cap_task_reparent_to_init,
135 + .task_setrlimit = apparmor_task_setrlimit,
137 .getprocattr = apparmor_getprocattr,
138 .setprocattr = apparmor_setprocattr,
139 --- a/security/apparmor/main.c
140 +++ b/security/apparmor/main.c
141 @@ -177,6 +177,9 @@ static int aa_audit_base(struct aa_profi
142 if (sa->request_mask)
143 audit_log_format(ab, " fsuid=%d", current->fsuid);
146 + audit_log_format(ab, " rlimit=%d", sa->rlimit - 1);
149 struct iattr *iattr = sa->iattr;
151 @@ -872,6 +875,79 @@ int aa_revalidate_sk(struct sock *sk, ch
156 + * aa_task_setrlimit - test permission to set an rlimit
157 + * @profile - profile confining the task
158 + * @resource - the resource being set
159 + * @new_rlim - the new resource limit
161 + * Control raising the processes hard limit.
163 +int aa_task_setrlimit(struct aa_profile *profile, unsigned int resource,
164 + struct rlimit *new_rlim)
166 + struct aa_audit sa;
169 + memset(&sa, 0, sizeof(sa));
170 + sa.operation = "setrlimit";
171 + sa.gfp_mask = GFP_KERNEL;
172 + sa.rlimit = resource + 1;
174 + if (profile->rlimits.mask & (1 << resource) &&
175 + new_rlim->rlim_max > profile->rlimits.limits[resource].rlim_max) {
176 + sa.error_code = -EACCES;
178 + error = aa_audit(profile, &sa);
184 +static int aa_rlimit_nproc(struct aa_profile *profile) {
185 + if (profile && (profile->rlimits.mask & (1 << RLIMIT_NPROC)) &&
186 + profile->task_count >= profile->rlimits.limits[RLIMIT_NPROC].rlim_max)
191 +void aa_set_rlimits(struct task_struct *task, struct aa_profile *profile)
198 + if (!profile->rlimits.mask)
201 + task_lock(task->group_leader);
203 + for (i = 0; i < RLIM_NLIMITS; i++, mask <<= 1) {
204 + struct rlimit new_rlim, *old_rlim;
206 + /* check to see if NPROC which is per profile and handled
207 + * in clone/exec or whether this is a limit to be set
208 + * can't set cpu limit either right now
210 + if (i == RLIMIT_NPROC || i == RLIMIT_CPU)
213 + old_rlim = task->signal->rlim + i;
214 + new_rlim = *old_rlim;
216 + if (mask & profile->rlimits.mask &&
217 + profile->rlimits.limits[i].rlim_max < new_rlim.rlim_max) {
218 + new_rlim.rlim_max = profile->rlimits.limits[i].rlim_max;
219 + /* soft limit should not exceed hard limit */
220 + if (new_rlim.rlim_cur > new_rlim.rlim_max)
221 + new_rlim.rlim_cur = new_rlim.rlim_max;
224 + *old_rlim = new_rlim;
226 + task_unlock(task->group_leader);
229 /*******************************
230 * Global task related functions
231 @@ -885,6 +961,7 @@ int aa_revalidate_sk(struct sock *sk, ch
233 int aa_clone(struct task_struct *child)
235 + struct aa_audit sa;
236 struct aa_task_context *cxt, *child_cxt;
237 struct aa_profile *profile;
239 @@ -894,6 +971,11 @@ int aa_clone(struct task_struct *child)
243 + memset(&sa, 0, sizeof(sa));
244 + sa.operation = "clone";
245 + sa.task = child->pid;
246 + sa.gfp_mask = GFP_KERNEL;
249 profile = aa_get_profile(current);
251 @@ -910,18 +992,22 @@ repeat:
255 + if (aa_rlimit_nproc(profile)) {
256 + sa.info = "rlimit nproc limit exceeded";
257 + unlock_profile(profile);
258 + aa_audit_reject(profile, &sa);
259 + aa_put_profile(profile);
263 /* No need to grab the child's task lock here. */
264 aa_change_task_context(child, child_cxt, profile,
265 cxt->cookie, cxt->previous_profile);
267 unlock_profile(profile);
269 if (APPARMOR_COMPLAIN(child_cxt) &&
270 profile == profile->ns->null_complain_profile) {
271 - struct aa_audit sa;
272 - memset(&sa, 0, sizeof(sa));
273 - sa.operation = "clone";
274 - sa.gfp_mask = GFP_KERNEL;
275 - sa.task = child->pid;
276 aa_audit_hint(profile, &sa);
278 aa_put_profile(profile);
279 @@ -1156,6 +1242,10 @@ repeat:
280 sa.task = current->parent->pid;
281 aa_audit_reject(profile, &sa);
283 + if (PTR_ERR(old_profile) == -EAGAIN) {
284 + sa.info = "rlimit nproc limit exceeded";
285 + aa_audit_reject(profile, &sa);
287 new_profile = old_profile;
290 @@ -1303,6 +1393,12 @@ static int do_change_profile(struct aa_p
294 + if ((error = aa_rlimit_nproc(new_profile))) {
295 + sa->info = "rlimit nproc limit exceeded";
296 + aa_audit_reject(cxt->profile, sa);
300 if (new_profile == ns->null_complain_profile)
301 aa_audit_hint(cxt->profile, sa);
303 @@ -1481,17 +1577,18 @@ struct aa_profile *__aa_replace_profile(
305 cxt = lock_task_and_profiles(task, profile);
306 if (unlikely(profile && profile->isstale)) {
308 - unlock_both_profiles(profile, cxt ? cxt->profile : NULL);
309 - aa_free_task_context(new_cxt);
310 - return ERR_PTR(-ESTALE);
311 + old_profile = ERR_PTR(-ESTALE);
315 if ((current->ptrace & PT_PTRACED) && aa_may_ptrace(cxt, profile)) {
317 - unlock_both_profiles(profile, cxt ? cxt->profile : NULL);
318 - aa_free_task_context(new_cxt);
319 - return ERR_PTR(-EPERM);
320 + old_profile = ERR_PTR(-EPERM);
324 + if (aa_rlimit_nproc(profile)) {
325 + old_profile = ERR_PTR(-EAGAIN);
330 @@ -1499,8 +1596,15 @@ struct aa_profile *__aa_replace_profile(
331 aa_change_task_context(task, new_cxt, profile, 0, NULL);
334 + aa_set_rlimits(task, profile);
335 unlock_both_profiles(profile, old_profile);
340 + unlock_both_profiles(profile, cxt ? cxt->profile : NULL);
341 + aa_free_task_context(new_cxt);
342 + return old_profile;
346 @@ -1565,6 +1669,7 @@ void aa_change_task_context(struct task_
349 list_del_init(&old_cxt->list);
350 + old_cxt->profile->task_count--;
351 call_rcu(&old_cxt->rcu, free_aa_task_context_rcu_callback);
354 @@ -1576,6 +1681,7 @@ void aa_change_task_context(struct task_
355 new_cxt->cookie = cookie;
356 new_cxt->task = task;
357 new_cxt->profile = aa_dup_profile(profile);
358 + profile->task_count++;
359 new_cxt->previous_profile = aa_dup_profile(previous_profile);
360 list_move(&new_cxt->list, &profile->task_contexts);
362 --- a/security/apparmor/module_interface.c
363 +++ b/security/apparmor/module_interface.c
364 @@ -177,6 +177,22 @@ fail:
368 +static int aa_is_u64(struct aa_ext *e, u64 *data, const char *name)
370 + void *pos = e->pos;
371 + if (aa_is_nameX(e, AA_U64, name)) {
372 + if (!aa_inbounds(e, sizeof(u64)))
375 + *data = le64_to_cpu(get_unaligned((u64 *)e->pos));
376 + e->pos += sizeof(u64);
384 static size_t aa_is_array(struct aa_ext *e, const char *name)
387 @@ -312,6 +328,39 @@ fail:
391 +int aa_unpack_rlimits(struct aa_ext *e, struct aa_profile *profile)
393 + void *pos = e->pos;
395 + /* rlimits are optional */
396 + if (aa_is_nameX(e, AA_STRUCT, "rlimits")) {
399 + if (!aa_is_u32(e, &tmp, NULL))
401 + profile->rlimits.mask = tmp;
403 + size = aa_is_array(e, NULL);
404 + if (size > RLIM_NLIMITS)
406 + for (i = 0; i < size; i++) {
408 + if (!aa_is_u64(e, &tmp, NULL))
410 + profile->rlimits.limits[i].rlim_max = tmp;
412 + if (!aa_is_nameX(e, AA_ARRAYEND, NULL))
414 + if (!aa_is_nameX(e, AA_STRUCTEND, NULL))
425 * aa_unpack_profile - unpack a serialized profile
426 * @e: serialized data extent information
427 @@ -355,6 +404,9 @@ static struct aa_profile *aa_unpack_prof
428 if (!aa_is_u32(e, &(profile->set_caps), NULL))
431 + if (!aa_unpack_rlimits(e, profile))
434 size = aa_is_array(e, "net_allowed_af");
437 @@ -614,6 +666,8 @@ ssize_t aa_replace_profile(void *udata,
438 sa.operation = "profile_load";
441 + /* do not fail replacement based off of profile's NPROC rlimit */
444 * Replacement needs to allocate a new aa_task_context for each
445 * task confined by old_profile. To do this the profile locks
446 @@ -634,6 +688,7 @@ ssize_t aa_replace_profile(void *udata,
448 task_replace(task, new_cxt, new_profile);
450 + aa_set_rlimits(task, new_profile);
453 unlock_both_profiles(old_profile, new_profile);
454 @@ -656,6 +711,7 @@ out:
456 * remove a profile from the profile list and all aa_task_context references
458 + * NOTE: removing confinement does not restore rlimits to preconfinemnet values
460 ssize_t aa_remove_profile(char *name, size_t size)