]> git.ipfire.org Git - people/pmueller/ipfire-2.x.git/blame - src/patches/suse-2.6.27.31/patches.apparmor/apparmor-rlimits.diff
Merge branch 'master' into next
[people/pmueller/ipfire-2.x.git] / src / patches / suse-2.6.27.31 / patches.apparmor / apparmor-rlimits.diff
CommitLineData
6a930a95
BS
1From: John Johansen <jjohansen@suse.de>
2Subject: AppArmor: per profile controls for system rlimits
3
4Provide contol of rlimits on a per profile basis. Each profile provides
5a per limit contol and corresponding hard limit value, such that when a
6profile becomes attached to a task it sets the tasks limits to be <= to
7the profiles specified limits. Note: the profile limit value will not
8raise a tasks limit if it is already less than the profile mandates.
9
10In addition to setting a tasks limits, the ability to set limits on
11a confined task are controlled. AppArmor only controls the raising
12of a tasks limits Tasks with CAP_SYS_RESOURCE can have their hard limits
13raised up to the value specified by the profile. AppArmor does not
14prevent a task for lowering its hard limits, nor does it provide
15additional control on soft limits.
16
17AppArmor only controls the limits specified in a profile so that
18any limit not specified is free to be modified subject to standard
19linux limitations.
20
21---
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(-)
28
29--- a/security/apparmor/apparmor.h
30+++ b/security/apparmor/apparmor.h
31@@ -16,6 +16,7 @@
32 #include <linux/fs.h>
33 #include <linux/binfmts.h>
34 #include <linux/rcupdate.h>
35+#include <linux/resource.h>
36 #include <linux/socket.h>
37 #include <net/sock.h>
38
39@@ -139,6 +140,18 @@ extern unsigned int apparmor_path_max;
40
41 #define AA_ERROR(fmt, args...) printk(KERN_ERR "AppArmor: " fmt, ##args)
42
43+/* struct aa_rlimit - rlimits settings for the profile
44+ * @mask: which hard limits to set
45+ * @limits: rlimit values that override task limits
46+ *
47+ * AppArmor rlimits are used to set confined task rlimits. Only the
48+ * limits specified in @mask will be controlled by apparmor.
49+ */
50+struct aa_rlimit {
51+ unsigned int mask;
52+ struct rlimit limits[RLIM_NLIMITS];
53+};
54+
55 struct aa_profile;
56
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;
70
71+ struct aa_rlimit rlimits;
72+ unsigned int task_count;
73+
74 struct kref count;
75 struct list_head task_contexts;
76 spinlock_t lock;
77@@ -261,6 +279,7 @@ struct aa_audit {
78 const char *name2;
79 const char *name3;
80 int request_mask, denied_mask, audit_mask;
81+ int rlimit;
82 struct iattr *iattr;
83 pid_t task, parent;
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);
92+
93
94 /* lsm.c */
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
99 {
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";
104
105 return simple_read_from_buffer(buf, size, ppos, features,
106 strlen(features));
107--- a/security/apparmor/lsm.c
108+++ b/security/apparmor/lsm.c
109@@ -883,6 +883,21 @@ static int apparmor_setprocattr(struct t
110 return error;
111 }
112
113+static int apparmor_task_setrlimit(unsigned int resource,
114+ struct rlimit *new_rlim)
115+{
116+ struct aa_profile *profile;
117+ int error = 0;
118+
119+ profile = aa_get_profile(current);
120+ if (profile) {
121+ error = aa_task_setrlimit(profile, resource, new_rlim);
122+ }
123+ aa_put_profile(profile);
124+
125+ return error;
126+}
127+
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,
136
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);
144
145+ if (sa->rlimit)
146+ audit_log_format(ab, " rlimit=%d", sa->rlimit - 1);
147+
148 if (sa->iattr) {
149 struct iattr *iattr = sa->iattr;
150
151@@ -872,6 +875,79 @@ int aa_revalidate_sk(struct sock *sk, ch
152
153 return error;
154 }
155+/**
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
160+ *
161+ * Control raising the processes hard limit.
162+ */
163+int aa_task_setrlimit(struct aa_profile *profile, unsigned int resource,
164+ struct rlimit *new_rlim)
165+{
166+ struct aa_audit sa;
167+ int error = 0;
168+
169+ memset(&sa, 0, sizeof(sa));
170+ sa.operation = "setrlimit";
171+ sa.gfp_mask = GFP_KERNEL;
172+ sa.rlimit = resource + 1;
173+
174+ if (profile->rlimits.mask & (1 << resource) &&
175+ new_rlim->rlim_max > profile->rlimits.limits[resource].rlim_max) {
176+ sa.error_code = -EACCES;
177+
178+ error = aa_audit(profile, &sa);
179+ }
180+
181+ return error;
182+}
183+
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)
187+ return -EAGAIN;
188+ return 0;
189+}
190+
191+void aa_set_rlimits(struct task_struct *task, struct aa_profile *profile)
192+{
193+ int i, mask;
194+
195+ if (!profile)
196+ return;
197+
198+ if (!profile->rlimits.mask)
199+ return;
200+
201+ task_lock(task->group_leader);
202+ mask = 1;
203+ for (i = 0; i < RLIM_NLIMITS; i++, mask <<= 1) {
204+ struct rlimit new_rlim, *old_rlim;
205+
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
209+ */
210+ if (i == RLIMIT_NPROC || i == RLIMIT_CPU)
211+ continue;
212+
213+ old_rlim = task->signal->rlim + i;
214+ new_rlim = *old_rlim;
215+
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;
222+ }
223+
224+ *old_rlim = new_rlim;
225+ }
226+ task_unlock(task->group_leader);
227+}
228
229 /*******************************
230 * Global task related functions
231@@ -885,6 +961,7 @@ int aa_revalidate_sk(struct sock *sk, ch
232 */
233 int aa_clone(struct task_struct *child)
234 {
235+ struct aa_audit sa;
236 struct aa_task_context *cxt, *child_cxt;
237 struct aa_profile *profile;
238
239@@ -894,6 +971,11 @@ int aa_clone(struct task_struct *child)
240 if (!child_cxt)
241 return -ENOMEM;
242
243+ memset(&sa, 0, sizeof(sa));
244+ sa.operation = "clone";
245+ sa.task = child->pid;
246+ sa.gfp_mask = GFP_KERNEL;
247+
248 repeat:
249 profile = aa_get_profile(current);
250 if (profile) {
251@@ -910,18 +992,22 @@ repeat:
252 goto repeat;
253 }
254
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);
260+ return -EAGAIN;
261+ }
262+
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);
266+
267 unlock_profile(profile);
268
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);
277 }
278 aa_put_profile(profile);
279@@ -1156,6 +1242,10 @@ repeat:
280 sa.task = current->parent->pid;
281 aa_audit_reject(profile, &sa);
282 }
283+ if (PTR_ERR(old_profile) == -EAGAIN) {
284+ sa.info = "rlimit nproc limit exceeded";
285+ aa_audit_reject(profile, &sa);
286+ }
287 new_profile = old_profile;
288 goto cleanup;
289 }
290@@ -1303,6 +1393,12 @@ static int do_change_profile(struct aa_p
291 goto out;
292 }
293
294+ if ((error = aa_rlimit_nproc(new_profile))) {
295+ sa->info = "rlimit nproc limit exceeded";
296+ aa_audit_reject(cxt->profile, sa);
297+ goto out;
298+ }
299+
300 if (new_profile == ns->null_complain_profile)
301 aa_audit_hint(cxt->profile, sa);
302
303@@ -1481,17 +1577,18 @@ struct aa_profile *__aa_replace_profile(
304
305 cxt = lock_task_and_profiles(task, profile);
306 if (unlikely(profile && profile->isstale)) {
307- task_unlock(task);
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);
312+ goto error;
313 }
314
315 if ((current->ptrace & PT_PTRACED) && aa_may_ptrace(cxt, profile)) {
316- task_unlock(task);
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);
321+ goto error;
322+ }
323+
324+ if (aa_rlimit_nproc(profile)) {
325+ old_profile = ERR_PTR(-EAGAIN);
326+ goto error;
327 }
328
329 if (cxt)
330@@ -1499,8 +1596,15 @@ struct aa_profile *__aa_replace_profile(
331 aa_change_task_context(task, new_cxt, profile, 0, NULL);
332
333 task_unlock(task);
334+ aa_set_rlimits(task, profile);
335 unlock_both_profiles(profile, old_profile);
336 return old_profile;
337+
338+error:
339+ task_unlock(task);
340+ unlock_both_profiles(profile, cxt ? cxt->profile : NULL);
341+ aa_free_task_context(new_cxt);
342+ return old_profile;
343 }
344
345 /**
346@@ -1565,6 +1669,7 @@ void aa_change_task_context(struct task_
347
348 if (old_cxt) {
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);
352 }
353 if (new_cxt) {
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);
361 }
362--- a/security/apparmor/module_interface.c
363+++ b/security/apparmor/module_interface.c
364@@ -177,6 +177,22 @@ fail:
365 return 0;
366 }
367
368+static int aa_is_u64(struct aa_ext *e, u64 *data, const char *name)
369+{
370+ void *pos = e->pos;
371+ if (aa_is_nameX(e, AA_U64, name)) {
372+ if (!aa_inbounds(e, sizeof(u64)))
373+ goto fail;
374+ if (data)
375+ *data = le64_to_cpu(get_unaligned((u64 *)e->pos));
376+ e->pos += sizeof(u64);
377+ return 1;
378+ }
379+fail:
380+ e->pos = pos;
381+ return 0;
382+}
383+
384 static size_t aa_is_array(struct aa_ext *e, const char *name)
385 {
386 void *pos = e->pos;
387@@ -312,6 +328,39 @@ fail:
388 return 0;
389 }
390
391+int aa_unpack_rlimits(struct aa_ext *e, struct aa_profile *profile)
392+{
393+ void *pos = e->pos;
394+
395+ /* rlimits are optional */
396+ if (aa_is_nameX(e, AA_STRUCT, "rlimits")) {
397+ int i, size;
398+ u32 tmp = 0;
399+ if (!aa_is_u32(e, &tmp, NULL))
400+ goto fail;
401+ profile->rlimits.mask = tmp;
402+
403+ size = aa_is_array(e, NULL);
404+ if (size > RLIM_NLIMITS)
405+ goto fail;
406+ for (i = 0; i < size; i++) {
407+ u64 tmp = 0;
408+ if (!aa_is_u64(e, &tmp, NULL))
409+ goto fail;
410+ profile->rlimits.limits[i].rlim_max = tmp;
411+ }
412+ if (!aa_is_nameX(e, AA_ARRAYEND, NULL))
413+ goto fail;
414+ if (!aa_is_nameX(e, AA_STRUCTEND, NULL))
415+ goto fail;
416+ }
417+ return 1;
418+
419+fail:
420+ e->pos = pos;
421+ return 0;
422+}
423+
424 /**
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))
429 goto fail;
430
431+ if (!aa_unpack_rlimits(e, profile))
432+ goto fail;
433+
434 size = aa_is_array(e, "net_allowed_af");
435 if (size) {
436 if (size > AF_MAX)
437@@ -614,6 +666,8 @@ ssize_t aa_replace_profile(void *udata,
438 sa.operation = "profile_load";
439 goto out;
440 }
441+ /* do not fail replacement based off of profile's NPROC rlimit */
442+
443 /*
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,
447 task_lock(task);
448 task_replace(task, new_cxt, new_profile);
449 task_unlock(task);
450+ aa_set_rlimits(task, new_profile);
451 new_cxt = NULL;
452 }
453 unlock_both_profiles(old_profile, new_profile);
454@@ -656,6 +711,7 @@ out:
455 *
456 * remove a profile from the profile list and all aa_task_context references
457 * to said profile.
458+ * NOTE: removing confinement does not restore rlimits to preconfinemnet values
459 */
460 ssize_t aa_remove_profile(char *name, size_t size)
461 {