]>
Commit | Line | Data |
---|---|---|
0776672e AM |
1 | commit 5ea33f587f5f7324c40c5986286d0f38307923bb |
2 | Author: John Johansen <john.johansen@canonical.com> | |
3 | Date: Mon Apr 11 16:55:10 2016 -0700 | |
1e8b8f9b | 4 | |
0776672e AM |
5 | apparmor: fix refcount bug in profile replacement |
6 | ||
7 | Signed-off-by: John Johansen <john.johansen@canonical.com> | |
8 | Acked-by: Seth Arnold <seth.arnold@canonical.com> | |
76514441 | 9 | |
0776672e AM |
10 | diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c |
11 | index 705c287..222052f 100644 | |
12 | --- a/security/apparmor/policy.c | |
13 | +++ b/security/apparmor/policy.c | |
14 | @@ -1189,12 +1189,12 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) | |
15 | aa_get_profile(newest); | |
16 | aa_put_profile(parent); | |
17 | rcu_assign_pointer(ent->new->parent, newest); | |
18 | - } else | |
19 | - aa_put_profile(newest); | |
20 | + } | |
21 | /* aafs interface uses replacedby */ | |
22 | rcu_assign_pointer(ent->new->replacedby->profile, | |
23 | aa_get_profile(ent->new)); | |
24 | __list_add_profile(&parent->base.profiles, ent->new); | |
25 | + aa_put_profile(newest); | |
26 | } else { | |
27 | /* aafs interface uses replacedby */ | |
28 | rcu_assign_pointer(ent->new->replacedby->profile, | |
29 | ||
30 | commit f65b1c9b72442e6166332c04f332e4b4d4797887 | |
31 | Author: John Johansen <john.johansen@canonical.com> | |
32 | Date: Mon Apr 11 16:57:19 2016 -0700 | |
33 | ||
34 | apparmor: fix replacement bug that adds new child to old parent | |
35 | ||
36 | When set atomic replacement is used and the parent is updated before the | |
37 | child, and the child did not exist in the old parent so there is no | |
38 | direct replacement then the new child is incorrectly added to the old | |
39 | parent. This results in the new parent not having the child(ren) that | |
40 | it should and the old parent when being destroyed asserting the | |
41 | following error. | |
42 | ||
43 | AppArmor: policy_destroy: internal error, policy '<profile/name>' still | |
44 | contains profiles | |
45 | ||
46 | Signed-off-by: John Johansen <john.johansen@canonical.com> | |
47 | Acked-by: Seth Arnold <seth.arnold@canonical.com> | |
48 | ||
49 | diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c | |
50 | index 222052f..c92a9f6 100644 | |
51 | --- a/security/apparmor/policy.c | |
52 | +++ b/security/apparmor/policy.c | |
53 | @@ -1193,7 +1193,7 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) | |
54 | /* aafs interface uses replacedby */ | |
55 | rcu_assign_pointer(ent->new->replacedby->profile, | |
56 | aa_get_profile(ent->new)); | |
57 | - __list_add_profile(&parent->base.profiles, ent->new); | |
58 | + __list_add_profile(&newest->base.profiles, ent->new); | |
59 | aa_put_profile(newest); | |
60 | } else { | |
61 | /* aafs interface uses replacedby */ | |
62 | ||
63 | commit b6669bef20c9d934bc6498e79fffa220f6226518 | |
64 | Author: John Johansen <john.johansen@canonical.com> | |
65 | Date: Sun Jun 8 11:20:54 2014 -0700 | |
66 | ||
67 | apparmor: fix uninitialized lsm_audit member | |
68 | ||
69 | BugLink: http://bugs.launchpad.net/bugs/1268727 | |
70 | ||
71 | The task field in the lsm_audit struct needs to be initialized if | |
72 | a change_hat fails, otherwise the following oops will occur | |
73 | ||
74 | BUG: unable to handle kernel paging request at 0000002fbead7d08 | |
75 | IP: [<ffffffff8171153e>] _raw_spin_lock+0xe/0x50 | |
76 | PGD 1e3f35067 PUD 0 | |
77 | Oops: 0002 [#1] SMP | |
78 | Modules linked in: pppox crc_ccitt p8023 p8022 psnap llc ax25 btrfs raid6_pq xor xfs libcrc32c dm_multipath scsi_dh kvm_amd dcdbas kvm microcode amd64_edac_mod joydev edac_core psmouse edac_mce_amd serio_raw k10temp sp5100_tco i2c_piix4 ipmi_si ipmi_msghandler acpi_power_meter mac_hid lp parport hid_generic usbhid hid pata_acpi mpt2sas ahci raid_class pata_atiixp bnx2 libahci scsi_transport_sas [last unloaded: tipc] | |
79 | CPU: 2 PID: 699 Comm: changehat_twice Tainted: GF O 3.13.0-7-generic #25-Ubuntu | |
80 | Hardware name: Dell Inc. PowerEdge R415/08WNM9, BIOS 1.8.6 12/06/2011 | |
81 | task: ffff8802135c6000 ti: ffff880212986000 task.ti: ffff880212986000 | |
82 | RIP: 0010:[<ffffffff8171153e>] [<ffffffff8171153e>] _raw_spin_lock+0xe/0x50 | |
83 | RSP: 0018:ffff880212987b68 EFLAGS: 00010006 | |
84 | RAX: 0000000000020000 RBX: 0000002fbead7500 RCX: 0000000000000000 | |
85 | RDX: 0000000000000292 RSI: ffff880212987ba8 RDI: 0000002fbead7d08 | |
86 | RBP: ffff880212987b68 R08: 0000000000000246 R09: ffff880216e572a0 | |
87 | R10: ffffffff815fd677 R11: ffffea0008469580 R12: ffffffff8130966f | |
88 | R13: ffff880212987ba8 R14: 0000002fbead7d08 R15: ffff8800d8c6b830 | |
89 | FS: 00002b5e6c84e7c0(0000) GS:ffff880216e40000(0000) knlGS:0000000055731700 | |
90 | CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 | |
91 | CR2: 0000002fbead7d08 CR3: 000000021270f000 CR4: 00000000000006e0 | |
92 | Stack: | |
93 | ffff880212987b98 ffffffff81075f17 ffffffff8130966f 0000000000000009 | |
94 | 0000000000000000 0000000000000000 ffff880212987bd0 ffffffff81075f7c | |
95 | 0000000000000292 ffff880212987c08 ffff8800d8c6b800 0000000000000026 | |
96 | Call Trace: | |
97 | [<ffffffff81075f17>] __lock_task_sighand+0x47/0x80 | |
98 | [<ffffffff8130966f>] ? apparmor_cred_prepare+0x2f/0x50 | |
99 | [<ffffffff81075f7c>] do_send_sig_info+0x2c/0x80 | |
100 | [<ffffffff81075fee>] send_sig_info+0x1e/0x30 | |
101 | [<ffffffff8130242d>] aa_audit+0x13d/0x190 | |
102 | [<ffffffff8130c1dc>] aa_audit_file+0xbc/0x130 | |
103 | [<ffffffff8130966f>] ? apparmor_cred_prepare+0x2f/0x50 | |
104 | [<ffffffff81304cc2>] aa_change_hat+0x202/0x530 | |
105 | [<ffffffff81308fc6>] aa_setprocattr_changehat+0x116/0x1d0 | |
106 | [<ffffffff8130a11d>] apparmor_setprocattr+0x25d/0x300 | |
107 | [<ffffffff812cee56>] security_setprocattr+0x16/0x20 | |
108 | [<ffffffff8121fc87>] proc_pid_attr_write+0x107/0x130 | |
109 | [<ffffffff811b7604>] vfs_write+0xb4/0x1f0 | |
110 | [<ffffffff811b8039>] SyS_write+0x49/0xa0 | |
111 | [<ffffffff8171a1bf>] tracesys+0xe1/0xe6 | |
112 | ||
113 | Signed-off-by: John Johansen <john.johansen@canonical.com> | |
114 | Acked-by: Seth Arnold <seth.arnold@canonical.com> | |
115 | ||
116 | diff --git a/security/apparmor/audit.c b/security/apparmor/audit.c | |
117 | index 89c7865..3a7f1da 100644 | |
118 | --- a/security/apparmor/audit.c | |
119 | +++ b/security/apparmor/audit.c | |
120 | @@ -200,7 +200,8 @@ int aa_audit(int type, struct aa_profile *profile, gfp_t gfp, | |
121 | ||
122 | if (sa->aad->type == AUDIT_APPARMOR_KILL) | |
123 | (void)send_sig_info(SIGKILL, NULL, | |
124 | - sa->u.tsk ? sa->u.tsk : current); | |
125 | + sa->type == LSM_AUDIT_DATA_TASK && sa->u.tsk ? | |
126 | + sa->u.tsk : current); | |
127 | ||
128 | if (sa->aad->type == AUDIT_APPARMOR_ALLOWED) | |
129 | return complain_error(sa->aad->error); | |
130 | diff --git a/security/apparmor/file.c b/security/apparmor/file.c | |
131 | index d186674..4d2af4b 100644 | |
132 | --- a/security/apparmor/file.c | |
133 | +++ b/security/apparmor/file.c | |
134 | @@ -110,7 +110,8 @@ int aa_audit_file(struct aa_profile *profile, struct file_perms *perms, | |
135 | int type = AUDIT_APPARMOR_AUTO; | |
136 | struct common_audit_data sa; | |
137 | struct apparmor_audit_data aad = {0,}; | |
138 | - sa.type = LSM_AUDIT_DATA_NONE; | |
139 | + sa.type = LSM_AUDIT_DATA_TASK; | |
140 | + sa.u.tsk = NULL; | |
141 | sa.aad = &aad; | |
142 | aad.op = op, | |
143 | aad.fs.request = request; | |
144 | ||
145 | commit aeab4cbfb86d0faeeb709e8201672e0662aa2c6f | |
146 | Author: John Johansen <john.johansen@canonical.com> | |
147 | Date: Fri Jul 25 04:02:03 2014 -0700 | |
148 | ||
149 | apparmor: exec should not be returning ENOENT when it denies | |
150 | ||
151 | The current behavior is confusing as it causes exec failures to report | |
152 | the executable is missing instead of identifying that apparmor | |
153 | caused the failure. | |
154 | ||
155 | Signed-off-by: John Johansen <john.johansen@canonical.com> | |
156 | Acked-by: Seth Arnold <seth.arnold@canonical.com> | |
157 | ||
158 | diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c | |
159 | index dc0027b..67a7418 100644 | |
160 | --- a/security/apparmor/domain.c | |
161 | +++ b/security/apparmor/domain.c | |
162 | @@ -433,7 +433,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) | |
163 | new_profile = aa_get_newest_profile(ns->unconfined); | |
164 | info = "ux fallback"; | |
165 | } else { | |
166 | - error = -ENOENT; | |
167 | + error = -EACCES; | |
168 | info = "profile not found"; | |
169 | /* remove MAY_EXEC to audit as failure */ | |
170 | perms.allow &= ~MAY_EXEC; | |
171 | ||
172 | commit 752e4263021d90cf23c262f2fd3ebfd6dbccd455 | |
173 | Author: John Johansen <john.johansen@canonical.com> | |
174 | Date: Fri Jul 25 04:01:56 2014 -0700 | |
175 | ||
176 | apparmor: fix update the mtime of the profile file on replacement | |
177 | ||
178 | Signed-off-by: John Johansen <john.johansen@canonical.com> | |
179 | Acked-by: Seth Arnold <seth.arnold@canonical.com> | |
180 | ||
181 | diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c | |
182 | index ad4fa49..45a6199 100644 | |
183 | --- a/security/apparmor/apparmorfs.c | |
184 | +++ b/security/apparmor/apparmorfs.c | |
185 | @@ -379,6 +379,8 @@ void __aa_fs_profile_migrate_dents(struct aa_profile *old, | |
186 | ||
187 | for (i = 0; i < AAFS_PROF_SIZEOF; i++) { | |
188 | new->dents[i] = old->dents[i]; | |
189 | + if (new->dents[i]) | |
190 | + new->dents[i]->d_inode->i_mtime = CURRENT_TIME; | |
191 | old->dents[i] = NULL; | |
192 | } | |
193 | } | |
194 | ||
195 | commit 0c67233b18406dc7a8629faf8f9452feace6fb13 | |
196 | Author: John Johansen <john.johansen@canonical.com> | |
197 | Date: Fri Jul 25 04:02:08 2014 -0700 | |
198 | ||
199 | apparmor: fix disconnected bind mnts reconnection | |
200 | ||
201 | Bind mounts can fail to be properly reconnected when PATH_CONNECT is | |
202 | specified. Ensure that when PATH_CONNECT is specified the path has | |
203 | a root. | |
204 | ||
205 | BugLink: http://bugs.launchpad.net/bugs/1319984 | |
206 | ||
207 | Signed-off-by: John Johansen <john.johansen@canonical.com> | |
208 | Acked-by: Seth Arnold <seth.arnold@canonical.com> | |
209 | ||
210 | diff --git a/security/apparmor/path.c b/security/apparmor/path.c | |
211 | index edddc02..f261678 100644 | |
212 | --- a/security/apparmor/path.c | |
213 | +++ b/security/apparmor/path.c | |
214 | @@ -141,7 +141,10 @@ static int d_namespace_path(const struct path *path, char *buf, int buflen, | |
215 | error = -EACCES; | |
216 | if (*res == '/') | |
217 | *name = res + 1; | |
218 | - } | |
219 | + } else if (*res != '/') | |
220 | + /* CONNECT_PATH with missing root */ | |
221 | + error = prepend(name, *name - buf, "/", 1); | |
222 | + | |
223 | } | |
224 | ||
225 | out: | |
226 | ||
227 | commit 30c2b759b4f456e97e859ca550666c8abe84ff3c | |
228 | Author: John Johansen <john.johansen@canonical.com> | |
229 | Date: Fri Jul 25 04:02:10 2014 -0700 | |
230 | ||
231 | apparmor: internal paths should be treated as disconnected | |
232 | ||
233 | Internal mounts are not mounted anywhere and as such should be treated | |
234 | as disconnected paths. | |
235 | ||
236 | Signed-off-by: John Johansen <john.johansen@canonical.com> | |
237 | Acked-by: Seth Arnold <seth.arnold@canonical.com> | |
238 | ||
239 | diff --git a/security/apparmor/path.c b/security/apparmor/path.c | |
240 | index f261678..a8fc7d0 100644 | |
241 | --- a/security/apparmor/path.c | |
242 | +++ b/security/apparmor/path.c | |
243 | @@ -25,7 +25,6 @@ | |
244 | #include "include/path.h" | |
245 | #include "include/policy.h" | |
246 | ||
247 | - | |
248 | /* modified from dcache.c */ | |
249 | static int prepend(char **buffer, int buflen, const char *str, int namelen) | |
250 | { | |
251 | @@ -39,6 +38,38 @@ static int prepend(char **buffer, int buflen, const char *str, int namelen) | |
252 | ||
253 | #define CHROOT_NSCONNECT (PATH_CHROOT_REL | PATH_CHROOT_NSCONNECT) | |
254 | ||
255 | +/* If the path is not connected to the expected root, | |
256 | + * check if it is a sysctl and handle specially else remove any | |
257 | + * leading / that __d_path may have returned. | |
258 | + * Unless | |
259 | + * specifically directed to connect the path, | |
260 | + * OR | |
261 | + * if in a chroot and doing chroot relative paths and the path | |
262 | + * resolves to the namespace root (would be connected outside | |
263 | + * of chroot) and specifically directed to connect paths to | |
264 | + * namespace root. | |
265 | + */ | |
266 | +static int disconnect(const struct path *path, char *buf, char **name, | |
267 | + int flags) | |
268 | +{ | |
269 | + int error = 0; | |
270 | + | |
271 | + if (!(flags & PATH_CONNECT_PATH) && | |
272 | + !(((flags & CHROOT_NSCONNECT) == CHROOT_NSCONNECT) && | |
273 | + our_mnt(path->mnt))) { | |
274 | + /* disconnected path, don't return pathname starting | |
275 | + * with '/' | |
276 | + */ | |
277 | + error = -EACCES; | |
278 | + if (**name == '/') | |
279 | + *name = *name + 1; | |
280 | + } else if (**name != '/') | |
281 | + /* CONNECT_PATH with missing root */ | |
282 | + error = prepend(name, *name - buf, "/", 1); | |
283 | + | |
284 | + return error; | |
285 | +} | |
286 | + | |
287 | /** | |
288 | * d_namespace_path - lookup a name associated with a given path | |
289 | * @path: path to lookup (NOT NULL) | |
290 | @@ -74,7 +105,8 @@ static int d_namespace_path(const struct path *path, char *buf, int buflen, | |
291 | * control instead of hard coded /proc | |
292 | */ | |
293 | return prepend(name, *name - buf, "/proc", 5); | |
294 | - } | |
295 | + } else | |
296 | + return disconnect(path, buf, name, flags); | |
297 | return 0; | |
298 | } | |
299 | ||
300 | @@ -120,32 +152,8 @@ static int d_namespace_path(const struct path *path, char *buf, int buflen, | |
301 | goto out; | |
302 | } | |
303 | ||
304 | - /* If the path is not connected to the expected root, | |
305 | - * check if it is a sysctl and handle specially else remove any | |
306 | - * leading / that __d_path may have returned. | |
307 | - * Unless | |
308 | - * specifically directed to connect the path, | |
309 | - * OR | |
310 | - * if in a chroot and doing chroot relative paths and the path | |
311 | - * resolves to the namespace root (would be connected outside | |
312 | - * of chroot) and specifically directed to connect paths to | |
313 | - * namespace root. | |
314 | - */ | |
315 | - if (!connected) { | |
316 | - if (!(flags & PATH_CONNECT_PATH) && | |
317 | - !(((flags & CHROOT_NSCONNECT) == CHROOT_NSCONNECT) && | |
318 | - our_mnt(path->mnt))) { | |
319 | - /* disconnected path, don't return pathname starting | |
320 | - * with '/' | |
321 | - */ | |
322 | - error = -EACCES; | |
323 | - if (*res == '/') | |
324 | - *name = res + 1; | |
325 | - } else if (*res != '/') | |
326 | - /* CONNECT_PATH with missing root */ | |
327 | - error = prepend(name, *name - buf, "/", 1); | |
328 | - | |
329 | - } | |
330 | + if (!connected) | |
331 | + error = disconnect(path, buf, name, flags); | |
332 | ||
333 | out: | |
334 | return error; | |
335 | ||
336 | commit 35f89b597a40c870f93a068bc92a7ef4f9b16a66 | |
337 | Author: John Johansen <john.johansen@canonical.com> | |
338 | Date: Sat Apr 16 13:59:02 2016 -0700 | |
339 | ||
340 | apparmor: fix put() parent ref after updating the active ref | |
341 | ||
342 | Signed-off-by: John Johansen <john.johansen@canonical.com> | |
343 | Acked-by: Seth Arnold <seth.arnold@canonical.com> | |
344 | ||
345 | diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c | |
346 | index c92a9f6..455c9f8 100644 | |
347 | --- a/security/apparmor/policy.c | |
348 | +++ b/security/apparmor/policy.c | |
349 | @@ -1187,8 +1187,8 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) | |
350 | /* parent replaced in this atomic set? */ | |
351 | if (newest != parent) { | |
352 | aa_get_profile(newest); | |
353 | - aa_put_profile(parent); | |
354 | rcu_assign_pointer(ent->new->parent, newest); | |
355 | + aa_put_profile(parent); | |
356 | } | |
357 | /* aafs interface uses replacedby */ | |
358 | rcu_assign_pointer(ent->new->replacedby->profile, | |
359 | ||
360 | commit 7b1ec6a04ca57fabe250f1102f2803dea7fbd03b | |
361 | Author: John Johansen <john.johansen@canonical.com> | |
362 | Date: Sat Apr 16 14:16:50 2016 -0700 | |
363 | ||
364 | apparmor: fix log failures for all profiles in a set | |
365 | ||
366 | currently only the profile that is causing the failure is logged. This | |
367 | makes it more confusing than necessary about which profiles loaded | |
368 | and which didn't. So make sure to log success and failure messages for | |
369 | all profiles in the set being loaded. | |
370 | ||
371 | Signed-off-by: John Johansen <john.johansen@canonical.com> | |
372 | Acked-by: Seth Arnold <seth.arnold@canonical.com> | |
373 | ||
374 | diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c | |
375 | index 455c9f8..db31bc5 100644 | |
376 | --- a/security/apparmor/policy.c | |
377 | +++ b/security/apparmor/policy.c | |
378 | @@ -1067,7 +1067,7 @@ static int __lookup_replace(struct aa_namespace *ns, const char *hname, | |
379 | */ | |
380 | ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) | |
381 | { | |
382 | - const char *ns_name, *name = NULL, *info = NULL; | |
383 | + const char *ns_name, *info = NULL; | |
384 | struct aa_namespace *ns = NULL; | |
385 | struct aa_load_ent *ent, *tmp; | |
386 | int op = OP_PROF_REPL; | |
387 | @@ -1082,18 +1082,15 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) | |
388 | /* released below */ | |
389 | ns = aa_prepare_namespace(ns_name); | |
390 | if (!ns) { | |
391 | - info = "failed to prepare namespace"; | |
392 | - error = -ENOMEM; | |
393 | - name = ns_name; | |
394 | - goto fail; | |
395 | + error = audit_policy(op, GFP_KERNEL, ns_name, | |
396 | + "failed to prepare namespace", -ENOMEM); | |
397 | + goto free; | |
398 | } | |
399 | ||
400 | mutex_lock(&ns->lock); | |
401 | /* setup parent and ns info */ | |
402 | list_for_each_entry(ent, &lh, list) { | |
403 | struct aa_policy *policy; | |
404 | - | |
405 | - name = ent->new->base.hname; | |
406 | error = __lookup_replace(ns, ent->new->base.hname, noreplace, | |
407 | &ent->old, &info); | |
408 | if (error) | |
409 | @@ -1121,7 +1118,6 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) | |
410 | if (!p) { | |
411 | error = -ENOENT; | |
412 | info = "parent does not exist"; | |
413 | - name = ent->new->base.hname; | |
414 | goto fail_lock; | |
415 | } | |
416 | rcu_assign_pointer(ent->new->parent, aa_get_profile(p)); | |
417 | @@ -1214,9 +1210,22 @@ out: | |
418 | ||
419 | fail_lock: | |
420 | mutex_unlock(&ns->lock); | |
421 | -fail: | |
422 | - error = audit_policy(op, GFP_KERNEL, name, info, error); | |
423 | ||
424 | + /* audit cause of failure */ | |
425 | + op = (!ent->old) ? OP_PROF_LOAD : OP_PROF_REPL; | |
426 | + audit_policy(op, GFP_KERNEL, ent->new->base.hname, info, error); | |
427 | + /* audit status that rest of profiles in the atomic set failed too */ | |
428 | + info = "valid profile in failed atomic policy load"; | |
429 | + list_for_each_entry(tmp, &lh, list) { | |
430 | + if (tmp == ent) { | |
431 | + info = "unchecked profile in failed atomic policy load"; | |
432 | + /* skip entry that caused failure */ | |
433 | + continue; | |
434 | + } | |
435 | + op = (!ent->old) ? OP_PROF_LOAD : OP_PROF_REPL; | |
436 | + audit_policy(op, GFP_KERNEL, tmp->new->base.hname, info, error); | |
437 | + } | |
438 | +free: | |
439 | list_for_each_entry_safe(ent, tmp, &lh, list) { | |
440 | list_del_init(&ent->list); | |
441 | aa_load_ent_free(ent); | |
442 | ||
443 | commit 4c475747a31b0637f0d47cb9bddaf2c6efb02854 | |
444 | Author: John Johansen <john.johansen@canonical.com> | |
445 | Date: Sat Apr 16 14:19:38 2016 -0700 | |
446 | ||
447 | apparmor: fix audit full profile hname on successful load | |
448 | ||
449 | Currently logging of a successful profile load only logs the basename | |
450 | of the profile. This can result in confusion when a child profile has | |
451 | the same name as the another profile in the set. Logging the hname | |
452 | will ensure there is no confusion. | |
453 | ||
454 | Signed-off-by: John Johansen <john.johansen@canonical.com> | |
455 | Acked-by: Seth Arnold <seth.arnold@canonical.com> | |
456 | ||
457 | diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c | |
458 | index db31bc5..ca402d0 100644 | |
459 | --- a/security/apparmor/policy.c | |
460 | +++ b/security/apparmor/policy.c | |
461 | @@ -1159,7 +1159,7 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) | |
462 | list_del_init(&ent->list); | |
463 | op = (!ent->old && !ent->rename) ? OP_PROF_LOAD : OP_PROF_REPL; | |
464 | ||
465 | - audit_policy(op, GFP_ATOMIC, ent->new->base.name, NULL, error); | |
466 | + audit_policy(op, GFP_ATOMIC, ent->new->base.hname, NULL, error); | |
467 | ||
468 | if (ent->old) { | |
469 | __replace_profile(ent->old, ent->new, 1); | |
470 | ||
471 | commit 430741dd766291d2e618b04e918ee6da844c230a | |
472 | Author: John Johansen <john.johansen@canonical.com> | |
473 | Date: Wed Apr 20 14:18:18 2016 -0700 | |
474 | ||
475 | apparmor: ensure the target profile name is always audited | |
476 | ||
477 | The target profile name was not being correctly audited in a few | |
478 | cases because the target variable was not being set and gotos | |
479 | passed the code to set it at apply: | |
480 | ||
481 | Since it is always based on new_profile just drop the target var | |
482 | and conditionally report based on new_profile. | |
483 | ||
484 | Signed-off-by: John Johansen <john.johansen@canonical.com> | |
485 | Acked-by: Seth Arnold <seth.arnold@canonical.com> | |
486 | ||
487 | diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c | |
488 | index 67a7418..fc3036b 100644 | |
489 | --- a/security/apparmor/domain.c | |
490 | +++ b/security/apparmor/domain.c | |
491 | @@ -346,7 +346,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) | |
492 | file_inode(bprm->file)->i_uid, | |
493 | file_inode(bprm->file)->i_mode | |
494 | }; | |
495 | - const char *name = NULL, *target = NULL, *info = NULL; | |
496 | + const char *name = NULL, *info = NULL; | |
497 | int error = 0; | |
498 | ||
499 | if (bprm->cred_prepared) | |
500 | @@ -399,6 +399,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) | |
501 | if (cxt->onexec) { | |
502 | struct file_perms cp; | |
503 | info = "change_profile onexec"; | |
504 | + new_profile = aa_get_newest_profile(cxt->onexec); | |
505 | if (!(perms.allow & AA_MAY_ONEXEC)) | |
506 | goto audit; | |
507 | ||
508 | @@ -413,7 +414,6 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) | |
509 | ||
510 | if (!(cp.allow & AA_MAY_ONEXEC)) | |
511 | goto audit; | |
512 | - new_profile = aa_get_newest_profile(cxt->onexec); | |
513 | goto apply; | |
514 | } | |
515 | ||
516 | @@ -445,10 +445,8 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) | |
517 | if (!new_profile) { | |
518 | error = -ENOMEM; | |
519 | info = "could not create null profile"; | |
520 | - } else { | |
521 | + } else | |
522 | error = -EACCES; | |
523 | - target = new_profile->base.hname; | |
524 | - } | |
525 | perms.xindex |= AA_X_UNSAFE; | |
526 | } else | |
527 | /* fail exec */ | |
528 | @@ -459,7 +457,6 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) | |
529 | * fail the exec. | |
530 | */ | |
531 | if (bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS) { | |
532 | - aa_put_profile(new_profile); | |
533 | error = -EPERM; | |
534 | goto cleanup; | |
535 | } | |
536 | @@ -474,10 +471,8 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) | |
537 | ||
538 | if (bprm->unsafe & (LSM_UNSAFE_PTRACE | LSM_UNSAFE_PTRACE_CAP)) { | |
539 | error = may_change_ptraced_domain(new_profile); | |
540 | - if (error) { | |
541 | - aa_put_profile(new_profile); | |
542 | + if (error) | |
543 | goto audit; | |
544 | - } | |
545 | } | |
546 | ||
547 | /* Determine if secure exec is needed. | |
548 | @@ -498,7 +493,6 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) | |
549 | bprm->unsafe |= AA_SECURE_X_NEEDED; | |
550 | } | |
551 | apply: | |
552 | - target = new_profile->base.hname; | |
553 | /* when transitioning profiles clear unsafe personality bits */ | |
554 | bprm->per_clear |= PER_CLEAR_ON_SETID; | |
555 | ||
556 | @@ -506,15 +500,19 @@ x_clear: | |
557 | aa_put_profile(cxt->profile); | |
558 | /* transfer new profile reference will be released when cxt is freed */ | |
559 | cxt->profile = new_profile; | |
560 | + new_profile = NULL; | |
561 | ||
562 | /* clear out all temporary/transitional state from the context */ | |
563 | aa_clear_task_cxt_trans(cxt); | |
564 | ||
565 | audit: | |
566 | error = aa_audit_file(profile, &perms, GFP_KERNEL, OP_EXEC, MAY_EXEC, | |
567 | - name, target, cond.uid, info, error); | |
568 | + name, | |
569 | + new_profile ? new_profile->base.hname : NULL, | |
570 | + cond.uid, info, error); | |
571 | ||
572 | cleanup: | |
573 | + aa_put_profile(new_profile); | |
574 | aa_put_profile(profile); | |
575 | kfree(buffer); | |
576 | ||
577 | ||
578 | commit 06763d057300b3d5bbe1894acfe236cf193bab78 | |
579 | Author: John Johansen <john.johansen@canonical.com> | |
580 | Date: Thu Mar 17 12:02:54 2016 -0700 | |
581 | ||
582 | apparmor: check that xindex is in trans_table bounds | |
583 | ||
584 | Signed-off-by: John Johansen <john.johansen@canonical.com> | |
585 | Acked-by: Seth Arnold <seth.arnold@canonical.com> | |
586 | ||
587 | diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c | |
588 | index a689f10..c841b12 100644 | |
589 | --- a/security/apparmor/policy_unpack.c | |
590 | +++ b/security/apparmor/policy_unpack.c | |
591 | @@ -676,7 +676,7 @@ static bool verify_xindex(int xindex, int table_size) | |
592 | int index, xtype; | |
593 | xtype = xindex & AA_X_TYPE_MASK; | |
594 | index = xindex & AA_X_INDEX_MASK; | |
595 | - if (xtype == AA_X_TABLE && index > table_size) | |
596 | + if (xtype == AA_X_TABLE && index >= table_size) | |
597 | return 0; | |
598 | return 1; | |
599 | } | |
600 | ||
0776672e AM |
601 | commit e13f968d154ba9d6a2c4f82f33d3312a63430b54 |
602 | Author: John Johansen <john.johansen@canonical.com> | |
603 | Date: Wed Dec 16 18:09:10 2015 -0800 | |
604 | ||
605 | apparmor: fix refcount race when finding a child profile | |
606 | ||
607 | When finding a child profile via an rcu critical section, the profile | |
608 | may be put and scheduled for deletion after the child is found but | |
609 | before its refcount is incremented. | |
610 | ||
611 | Protect against this by repeating the lookup if the profiles refcount | |
612 | is 0 and is one its way to deletion. | |
613 | ||
614 | Signed-off-by: John Johansen <john.johansen@canonical.com> | |
615 | Acked-by: Seth Arnold <seth.arnold@canonical.com> | |
616 | ||
617 | diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c | |
618 | index ca402d0..7807125 100644 | |
619 | --- a/security/apparmor/policy.c | |
620 | +++ b/security/apparmor/policy.c | |
621 | @@ -766,7 +766,9 @@ struct aa_profile *aa_find_child(struct aa_profile *parent, const char *name) | |
622 | struct aa_profile *profile; | |
623 | ||
624 | rcu_read_lock(); | |
625 | - profile = aa_get_profile(__find_child(&parent->base.profiles, name)); | |
626 | + do { | |
627 | + profile = __find_child(&parent->base.profiles, name); | |
628 | + } while (profile && !aa_get_profile_not0(profile)); | |
629 | rcu_read_unlock(); | |
630 | ||
631 | /* refcount released by caller */ | |
632 | ||
633 | commit 5833ccff1227fbc8f1bab64351f6747a6c71bdeb | |
634 | Author: Geliang Tang <geliangtang@163.com> | |
635 | Date: Mon Nov 16 21:46:33 2015 +0800 | |
636 | ||
637 | apparmor: use list_next_entry instead of list_entry_next | |
638 | ||
639 | list_next_entry has been defined in list.h, so I replace list_entry_next | |
640 | with it. | |
641 | ||
642 | Signed-off-by: Geliang Tang <geliangtang@163.com> | |
643 | Acked-by: Serge Hallyn <serge.hallyn@canonical.com> | |
644 | Signed-off-by: John Johansen <john.johansen@canonical.com> | |
645 | ||
646 | diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c | |
647 | index 0d8dd71..729e595 100644 | |
648 | --- a/security/apparmor/apparmorfs.c | |
649 | +++ b/security/apparmor/apparmorfs.c | |
650 | @@ -553,8 +553,6 @@ fail2: | |
651 | } | |
652 | ||
653 | ||
654 | -#define list_entry_next(pos, member) \ | |
655 | - list_entry(pos->member.next, typeof(*pos), member) | |
656 | #define list_entry_is_head(pos, head, member) (&pos->member == (head)) | |
657 | ||
658 | /** | |
659 | @@ -585,7 +583,7 @@ static struct aa_namespace *__next_namespace(struct aa_namespace *root, | |
660 | parent = ns->parent; | |
661 | while (ns != root) { | |
662 | mutex_unlock(&ns->lock); | |
663 | - next = list_entry_next(ns, base.list); | |
664 | + next = list_next_entry(ns, base.list); | |
665 | if (!list_entry_is_head(next, &parent->sub_ns, base.list)) { | |
666 | mutex_lock(&next->lock); | |
667 | return next; | |
668 | @@ -639,7 +637,7 @@ static struct aa_profile *__next_profile(struct aa_profile *p) | |
669 | parent = rcu_dereference_protected(p->parent, | |
670 | mutex_is_locked(&p->ns->lock)); | |
671 | while (parent) { | |
672 | - p = list_entry_next(p, base.list); | |
673 | + p = list_next_entry(p, base.list); | |
674 | if (!list_entry_is_head(p, &parent->base.profiles, base.list)) | |
675 | return p; | |
676 | p = parent; | |
677 | @@ -648,7 +646,7 @@ static struct aa_profile *__next_profile(struct aa_profile *p) | |
678 | } | |
679 | ||
680 | /* is next another profile in the namespace */ | |
681 | - p = list_entry_next(p, base.list); | |
682 | + p = list_next_entry(p, base.list); | |
683 | if (!list_entry_is_head(p, &ns->base.profiles, base.list)) | |
684 | return p; | |
685 | ||
686 | ||
687 | commit 645801f1ddd183109c011e5ecee23ed3fdcae244 | |
688 | Author: Jeff Mahoney <jeffm@suse.com> | |
689 | Date: Fri Nov 6 15:17:30 2015 -0500 | |
690 | ||
691 | apparmor: allow SYS_CAP_RESOURCE to be sufficient to prlimit another task | |
692 | ||
693 | While using AppArmor, SYS_CAP_RESOURCE is insufficient to call prlimit | |
694 | on another task. The only other example of a AppArmor mediating access to | |
695 | another, already running, task (ignoring fork+exec) is ptrace. | |
696 | ||
697 | The AppArmor model for ptrace is that one of the following must be true: | |
698 | 1) The tracer is unconfined | |
699 | 2) The tracer is in complain mode | |
700 | 3) The tracer and tracee are confined by the same profile | |
701 | 4) The tracer is confined but has SYS_CAP_PTRACE | |
702 | ||
703 | 1), 2, and 3) are already true for setrlimit. | |
704 | ||
705 | We can match the ptrace model just by allowing CAP_SYS_RESOURCE. | |
706 | ||
707 | We still test the values of the rlimit since it can always be overridden | |
708 | using a value that means unlimited for a particular resource. | |
709 | ||
710 | Signed-off-by: Jeff Mahoney <jeffm@suse.com> | |
711 | Signed-off-by: John Johansen <john.johansen@canonical.com> | |
712 | ||
713 | diff --git a/security/apparmor/resource.c b/security/apparmor/resource.c | |
714 | index 748bf0c..67a6072 100644 | |
715 | --- a/security/apparmor/resource.c | |
716 | +++ b/security/apparmor/resource.c | |
717 | @@ -101,9 +101,11 @@ int aa_task_setrlimit(struct aa_profile *profile, struct task_struct *task, | |
718 | /* TODO: extend resource control to handle other (non current) | |
719 | * profiles. AppArmor rules currently have the implicit assumption | |
720 | * that the task is setting the resource of a task confined with | |
721 | - * the same profile. | |
722 | + * the same profile or that the task setting the resource of another | |
723 | + * task has CAP_SYS_RESOURCE. | |
724 | */ | |
725 | - if (profile != task_profile || | |
726 | + if ((profile != task_profile && | |
727 | + aa_capable(profile, CAP_SYS_RESOURCE, 1)) || | |
728 | (profile->rlimits.mask & (1 << resource) && | |
729 | new_rlim->rlim_max > profile->rlimits.limits[resource].rlim_max)) | |
730 | error = -EACCES; | |
731 | ||
732 | commit 2be4aed1f3332d87273eb593944332054f3cffac | |
733 | Author: John Johansen <john.johansen@canonical.com> | |
734 | Date: Thu Jun 2 02:37:02 2016 -0700 | |
735 | ||
736 | apparmor: add missing id bounds check on dfa verification | |
737 | ||
738 | Signed-off-by: John Johansen <john.johansen@canonical.com> | |
739 | ||
740 | diff --git a/security/apparmor/include/match.h b/security/apparmor/include/match.h | |
741 | index 001c43a..a1c04fe 100644 | |
742 | --- a/security/apparmor/include/match.h | |
743 | +++ b/security/apparmor/include/match.h | |
744 | @@ -62,6 +62,7 @@ struct table_set_header { | |
745 | #define YYTD_ID_ACCEPT2 6 | |
746 | #define YYTD_ID_NXT 7 | |
747 | #define YYTD_ID_TSIZE 8 | |
748 | +#define YYTD_ID_MAX 8 | |
749 | ||
750 | #define YYTD_DATA8 1 | |
751 | #define YYTD_DATA16 2 | |
752 | diff --git a/security/apparmor/match.c b/security/apparmor/match.c | |
753 | index 727eb42..f9f57c6 100644 | |
754 | --- a/security/apparmor/match.c | |
755 | +++ b/security/apparmor/match.c | |
756 | @@ -47,6 +47,8 @@ static struct table_header *unpack_table(char *blob, size_t bsize) | |
757 | * it every time we use td_id as an index | |
758 | */ | |
759 | th.td_id = be16_to_cpu(*(u16 *) (blob)) - 1; | |
760 | + if (th.td_id > YYTD_ID_MAX) | |
761 | + goto out; | |
762 | th.td_flags = be16_to_cpu(*(u16 *) (blob + 2)); | |
763 | th.td_lolen = be32_to_cpu(*(u32 *) (blob + 8)); | |
764 | blob += sizeof(struct table_header); | |
765 | ||
766 | commit c7f87d3c3363b1a0c4724e627e5c8e640a883c89 | |
767 | Author: John Johansen <john.johansen@canonical.com> | |
768 | Date: Wed Jun 15 09:57:55 2016 +0300 | |
769 | ||
770 | apparmor: don't check for vmalloc_addr if kvzalloc() failed | |
771 | ||
772 | Signed-off-by: John Johansen <john.johansen@canonical.com> | |
773 | ||
774 | diff --git a/security/apparmor/match.c b/security/apparmor/match.c | |
775 | index f9f57c6..32b72eb 100644 | |
776 | --- a/security/apparmor/match.c | |
777 | +++ b/security/apparmor/match.c | |
778 | @@ -75,14 +75,14 @@ static struct table_header *unpack_table(char *blob, size_t bsize) | |
779 | u32, be32_to_cpu); | |
780 | else | |
781 | goto fail; | |
782 | + /* if table was vmalloced make sure the page tables are synced | |
783 | + * before it is used, as it goes live to all cpus. | |
784 | + */ | |
785 | + if (is_vmalloc_addr(table)) | |
786 | + vm_unmap_aliases(); | |
787 | } | |
788 | ||
789 | out: | |
790 | - /* if table was vmalloced make sure the page tables are synced | |
791 | - * before it is used, as it goes live to all cpus. | |
792 | - */ | |
793 | - if (is_vmalloc_addr(table)) | |
794 | - vm_unmap_aliases(); | |
795 | return table; | |
796 | fail: | |
797 | kvfree(table); | |
798 | ||
799 | commit 0f7e61013dd1e67ebb54d58eee11ab009ceb5ef3 | |
800 | Author: John Johansen <john.johansen@canonical.com> | |
801 | Date: Wed Jun 15 10:00:55 2016 +0300 | |
802 | ||
803 | apparmor: fix oops in profile_unpack() when policy_db is not present | |
804 | ||
805 | BugLink: http://bugs.launchpad.net/bugs/1592547 | |
806 | ||
807 | If unpack_dfa() returns NULL due to the dfa not being present, | |
808 | profile_unpack() is not checking if the dfa is not present (NULL). | |
809 | ||
810 | Signed-off-by: John Johansen <john.johansen@canonical.com> | |
811 | ||
812 | diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c | |
813 | index c841b12..dac2121 100644 | |
814 | --- a/security/apparmor/policy_unpack.c | |
815 | +++ b/security/apparmor/policy_unpack.c | |
816 | @@ -583,6 +583,9 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) | |
817 | error = PTR_ERR(profile->policy.dfa); | |
818 | profile->policy.dfa = NULL; | |
819 | goto fail; | |
820 | + } else if (!profile->policy.dfa) { | |
821 | + error = -EPROTO; | |
822 | + goto fail; | |
823 | } | |
824 | if (!unpack_u32(e, &profile->policy.start[0], "start")) | |
825 | /* default start state */ | |
826 | ||
827 | commit de4ca46ec035283928e8fa40797897cefcf6ec3e | |
828 | Author: John Johansen <john.johansen@canonical.com> | |
829 | Date: Wed Jun 22 18:01:08 2016 -0700 | |
830 | ||
831 | apparmor: fix module parameters can be changed after policy is locked | |
832 | ||
833 | the policy_lock parameter is a one way switch that prevents policy | |
834 | from being further modified. Unfortunately some of the module parameters | |
835 | can effectively modify policy by turning off enforcement. | |
836 | ||
837 | split policy_admin_capable into a view check and a full admin check, | |
838 | and update the admin check to test the policy_lock parameter. | |
839 | ||
840 | Signed-off-by: John Johansen <john.johansen@canonical.com> | |
841 | ||
842 | diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h | |
843 | index c28b0f2..52275f0 100644 | |
844 | --- a/security/apparmor/include/policy.h | |
845 | +++ b/security/apparmor/include/policy.h | |
846 | @@ -403,6 +403,8 @@ static inline int AUDIT_MODE(struct aa_profile *profile) | |
847 | return profile->audit; | |
848 | } | |
849 | ||
850 | +bool policy_view_capable(void); | |
851 | +bool policy_admin_capable(void); | |
852 | bool aa_may_manage_policy(int op); | |
853 | ||
854 | #endif /* __AA_POLICY_H */ | |
855 | diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c | |
856 | index 7798e16..e83eefb 100644 | |
857 | --- a/security/apparmor/lsm.c | |
858 | +++ b/security/apparmor/lsm.c | |
859 | @@ -728,51 +728,49 @@ __setup("apparmor=", apparmor_enabled_setup); | |
860 | /* set global flag turning off the ability to load policy */ | |
861 | static int param_set_aalockpolicy(const char *val, const struct kernel_param *kp) | |
862 | { | |
863 | - if (!capable(CAP_MAC_ADMIN)) | |
864 | + if (!policy_admin_capable()) | |
865 | return -EPERM; | |
866 | - if (aa_g_lock_policy) | |
867 | - return -EACCES; | |
868 | return param_set_bool(val, kp); | |
869 | } | |
870 | ||
871 | static int param_get_aalockpolicy(char *buffer, const struct kernel_param *kp) | |
872 | { | |
873 | - if (!capable(CAP_MAC_ADMIN)) | |
874 | + if (!policy_view_capable()) | |
875 | return -EPERM; | |
876 | return param_get_bool(buffer, kp); | |
877 | } | |
878 | ||
879 | static int param_set_aabool(const char *val, const struct kernel_param *kp) | |
880 | { | |
881 | - if (!capable(CAP_MAC_ADMIN)) | |
882 | + if (!policy_admin_capable()) | |
883 | return -EPERM; | |
884 | return param_set_bool(val, kp); | |
885 | } | |
886 | ||
887 | static int param_get_aabool(char *buffer, const struct kernel_param *kp) | |
888 | { | |
889 | - if (!capable(CAP_MAC_ADMIN)) | |
890 | + if (!policy_view_capable()) | |
891 | return -EPERM; | |
892 | return param_get_bool(buffer, kp); | |
893 | } | |
894 | ||
895 | static int param_set_aauint(const char *val, const struct kernel_param *kp) | |
896 | { | |
897 | - if (!capable(CAP_MAC_ADMIN)) | |
898 | + if (!policy_admin_capable()) | |
899 | return -EPERM; | |
900 | return param_set_uint(val, kp); | |
901 | } | |
902 | ||
903 | static int param_get_aauint(char *buffer, const struct kernel_param *kp) | |
904 | { | |
905 | - if (!capable(CAP_MAC_ADMIN)) | |
906 | + if (!policy_view_capable()) | |
907 | return -EPERM; | |
908 | return param_get_uint(buffer, kp); | |
909 | } | |
910 | ||
911 | static int param_get_audit(char *buffer, struct kernel_param *kp) | |
912 | { | |
913 | - if (!capable(CAP_MAC_ADMIN)) | |
914 | + if (!policy_view_capable()) | |
915 | return -EPERM; | |
916 | ||
917 | if (!apparmor_enabled) | |
918 | @@ -784,7 +782,7 @@ static int param_get_audit(char *buffer, struct kernel_param *kp) | |
919 | static int param_set_audit(const char *val, struct kernel_param *kp) | |
920 | { | |
921 | int i; | |
922 | - if (!capable(CAP_MAC_ADMIN)) | |
923 | + if (!policy_admin_capable()) | |
924 | return -EPERM; | |
925 | ||
926 | if (!apparmor_enabled) | |
927 | @@ -805,7 +803,7 @@ static int param_set_audit(const char *val, struct kernel_param *kp) | |
928 | ||
929 | static int param_get_mode(char *buffer, struct kernel_param *kp) | |
930 | { | |
931 | - if (!capable(CAP_MAC_ADMIN)) | |
932 | + if (!policy_admin_capable()) | |
933 | return -EPERM; | |
934 | ||
935 | if (!apparmor_enabled) | |
936 | @@ -817,7 +815,7 @@ static int param_get_mode(char *buffer, struct kernel_param *kp) | |
937 | static int param_set_mode(const char *val, struct kernel_param *kp) | |
938 | { | |
939 | int i; | |
940 | - if (!capable(CAP_MAC_ADMIN)) | |
941 | + if (!policy_admin_capable()) | |
942 | return -EPERM; | |
943 | ||
944 | if (!apparmor_enabled) | |
945 | diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c | |
946 | index 7807125..179e68d 100644 | |
947 | --- a/security/apparmor/policy.c | |
948 | +++ b/security/apparmor/policy.c | |
949 | @@ -918,6 +918,22 @@ static int audit_policy(int op, gfp_t gfp, const char *name, const char *info, | |
950 | &sa, NULL); | |
951 | } | |
952 | ||
953 | +bool policy_view_capable(void) | |
954 | +{ | |
955 | + struct user_namespace *user_ns = current_user_ns(); | |
956 | + bool response = false; | |
957 | + | |
958 | + if (ns_capable(user_ns, CAP_MAC_ADMIN)) | |
959 | + response = true; | |
960 | + | |
961 | + return response; | |
962 | +} | |
963 | + | |
964 | +bool policy_admin_capable(void) | |
965 | +{ | |
966 | + return policy_view_capable() && !aa_g_lock_policy; | |
967 | +} | |
968 | + | |
969 | /** | |
970 | * aa_may_manage_policy - can the current task manage policy | |
971 | * @op: the policy manipulation operation being done | |
972 | @@ -932,7 +948,7 @@ bool aa_may_manage_policy(int op) | |
973 | return 0; | |
974 | } | |
975 | ||
976 | - if (!capable(CAP_MAC_ADMIN)) { | |
977 | + if (!policy_admin_capable()) { | |
978 | audit_policy(op, GFP_KERNEL, NULL, "not policy admin", -EACCES); | |
979 | return 0; | |
980 | } | |
981 | ||
982 | commit 46c339f46b83e4cf8098f599cd182e65e9d054fc | |
983 | Author: Heinrich Schuchardt <xypron.glpk@gmx.de> | |
984 | Date: Fri Jun 10 23:34:26 2016 +0200 | |
985 | ||
986 | apparmor: do not expose kernel stack | |
987 | ||
988 | Do not copy uninitalized fields th.td_hilen, th.td_data. | |
989 | ||
990 | Signed-off-by: Heinrich Schuchardt <xypron.glpk@gmx.de> | |
991 | Signed-off-by: John Johansen <john.johansen@canonical.com> | |
992 | ||
993 | diff --git a/security/apparmor/match.c b/security/apparmor/match.c | |
994 | index 32b72eb..3f900fc 100644 | |
995 | --- a/security/apparmor/match.c | |
996 | +++ b/security/apparmor/match.c | |
997 | @@ -63,7 +63,9 @@ static struct table_header *unpack_table(char *blob, size_t bsize) | |
998 | ||
999 | table = kvzalloc(tsize); | |
1000 | if (table) { | |
1001 | - *table = th; | |
1002 | + table->td_id = th.td_id; | |
1003 | + table->td_flags = th.td_flags; | |
1004 | + table->td_lolen = th.td_lolen; | |
1005 | if (th.td_flags == YYTD_DATA8) | |
1006 | UNPACK_ARRAY(table->td_data, blob, th.td_lolen, | |
1007 | u8, byte_to_byte); | |
1008 | ||
1009 | commit 7e65e8142b2ea4891581173d6e92fc337b02ff8b | |
1010 | Author: John Johansen <john.johansen@canonical.com> | |
1011 | Date: Sat Jul 9 23:46:33 2016 -0700 | |
1012 | ||
1013 | apparmor: fix arg_size computation for when setprocattr is null terminated | |
1014 | ||
1015 | Signed-off-by: John Johansen <john.johansen@canonical.com> | |
1016 | ||
1017 | diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c | |
1018 | index e83eefb..ba8207b 100644 | |
1019 | --- a/security/apparmor/lsm.c | |
1020 | +++ b/security/apparmor/lsm.c | |
1021 | @@ -529,7 +529,7 @@ static int apparmor_setprocattr(struct task_struct *task, char *name, | |
1022 | if (!*args) | |
1023 | goto out; | |
1024 | ||
1025 | - arg_size = size - (args - (char *) value); | |
1026 | + arg_size = size - (args - (largs ? largs : (char *) value)); | |
1027 | if (strcmp(name, "current") == 0) { | |
1028 | if (strcmp(command, "changehat") == 0) { | |
1029 | error = aa_setprocattr_changehat(args, arg_size, | |
1030 | ||
1031 | commit b661b13237991be6b5cdf0849f137c5ec58217bf | |
1032 | Author: John Johansen <john.johansen@canonical.com> | |
1033 | Date: Mon Oct 4 15:03:36 2010 -0700 | |
1034 | ||
1035 | UBUNTU: SAUCE: AppArmor: basic networking rules | |
1036 | ||
1037 | Base support for network mediation. | |
1038 | ||
1039 | Signed-off-by: John Johansen <john.johansen@canonical.com> | |
5882c9d4 | 1040 | |
5882c9d4 AM |
1041 | diff --git a/security/apparmor/.gitignore b/security/apparmor/.gitignore |
1042 | index 9cdec70..d5b291e 100644 | |
1043 | --- a/security/apparmor/.gitignore | |
1044 | +++ b/security/apparmor/.gitignore | |
1045 | @@ -1,5 +1,6 @@ | |
1046 | # | |
1047 | # Generated include files | |
1048 | # | |
1049 | +net_names.h | |
1050 | capability_names.h | |
1051 | rlim_names.h | |
fc63ffa9 | 1052 | diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile |
5882c9d4 | 1053 | index d693df8..5dbb72f 100644 |
fc63ffa9 AM |
1054 | --- a/security/apparmor/Makefile |
1055 | +++ b/security/apparmor/Makefile | |
5882c9d4 | 1056 | @@ -4,10 +4,10 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o |
ceaf2cfb | 1057 | |
fc63ffa9 AM |
1058 | apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \ |
1059 | path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \ | |
1060 | - resource.o sid.o file.o | |
1061 | + resource.o sid.o file.o net.o | |
5882c9d4 | 1062 | apparmor-$(CONFIG_SECURITY_APPARMOR_HASH) += crypto.o |
ceaf2cfb | 1063 | |
948a1326 | 1064 | -clean-files := capability_names.h rlim_names.h |
1e8b8f9b | 1065 | +clean-files := capability_names.h rlim_names.h net_names.h |
ceaf2cfb | 1066 | |
ceaf2cfb | 1067 | |
948a1326 | 1068 | # Build a lower case string table of capability names |
5882c9d4 AM |
1069 | @@ -25,6 +25,38 @@ cmd_make-caps = echo "static const char *const capability_names[] = {" > $@ ;\ |
1070 | -e 's/^\#define[ \t]+CAP_([A-Z0-9_]+)[ \t]+([0-9]+)/\L\1/p' | \ | |
1071 | tr '\n' ' ' | sed -e 's/ $$/"\n/' >> $@ | |
948a1326 | 1072 | |
1e8b8f9b | 1073 | +# Build a lower case string table of address family names |
948a1326 | 1074 | +# Transform lines from |
1e8b8f9b AM |
1075 | +# define AF_LOCAL 1 /* POSIX name for AF_UNIX */ |
1076 | +# #define AF_INET 2 /* Internet IP Protocol */ | |
1077 | +# to | |
1078 | +# [1] = "local", | |
1079 | +# [2] = "inet", | |
1080 | +# | |
1081 | +# and build the securityfs entries for the mapping. | |
1082 | +# Transforms lines from | |
1083 | +# #define AF_INET 2 /* Internet IP Protocol */ | |
948a1326 | 1084 | +# to |
1e8b8f9b | 1085 | +# #define AA_FS_AF_MASK "local inet" |
ceaf2cfb | 1086 | +quiet_cmd_make-af = GEN $@ |
948a1326 | 1087 | +cmd_make-af = echo "static const char *address_family_names[] = {" > $@ ;\ |
1e8b8f9b AM |
1088 | + sed $< >>$@ -r -n -e "/AF_MAX/d" -e "/AF_LOCAL/d" -e \ |
1089 | + 's/^\#define[ \t]+AF_([A-Z0-9_]+)[ \t]+([0-9]+)(.*)/[\2] = "\L\1",/p';\ | |
1090 | + echo "};" >> $@ ;\ | |
1091 | + echo -n '\#define AA_FS_AF_MASK "' >> $@ ;\ | |
1092 | + sed -r -n 's/^\#define[ \t]+AF_([A-Z0-9_]+)[ \t]+([0-9]+)(.*)/\L\1/p'\ | |
1093 | + $< | tr '\n' ' ' | sed -e 's/ $$/"\n/' >> $@ | |
1094 | + | |
1095 | +# Build a lower case string table of sock type names | |
1096 | +# Transform lines from | |
1097 | +# SOCK_STREAM = 1, | |
1098 | +# to | |
1099 | +# [1] = "stream", | |
1100 | +quiet_cmd_make-sock = GEN $@ | |
1101 | +cmd_make-sock = echo "static const char *sock_type_names[] = {" >> $@ ;\ | |
1102 | + sed $^ >>$@ -r -n \ | |
1103 | + -e 's/^\tSOCK_([A-Z0-9_]+)[\t]+=[ \t]+([0-9]+)(.*)/[\2] = "\L\1",/p';\ | |
948a1326 | 1104 | + echo "};" >> $@ |
1e8b8f9b AM |
1105 | |
1106 | # Build a lower case string table of rlimit names. | |
1107 | # Transforms lines from | |
5882c9d4 | 1108 | @@ -61,6 +93,7 @@ cmd_make-rlim = echo "static const char *const rlim_names[RLIM_NLIMITS] = {" \ |
1e8b8f9b AM |
1109 | tr '\n' ' ' | sed -e 's/ $$/"\n/' >> $@ |
1110 | ||
fc63ffa9 | 1111 | $(obj)/capability.o : $(obj)/capability_names.h |
1e8b8f9b | 1112 | +$(obj)/net.o : $(obj)/net_names.h |
fc63ffa9 | 1113 | $(obj)/resource.o : $(obj)/rlim_names.h |
5882c9d4 | 1114 | $(obj)/capability_names.h : $(srctree)/include/uapi/linux/capability.h \ |
1e8b8f9b | 1115 | $(src)/Makefile |
5882c9d4 AM |
1116 | @@ -68,3 +101,8 @@ $(obj)/capability_names.h : $(srctree)/include/uapi/linux/capability.h \ |
1117 | $(obj)/rlim_names.h : $(srctree)/include/uapi/asm-generic/resource.h \ | |
1e8b8f9b | 1118 | $(src)/Makefile |
948a1326 | 1119 | $(call cmd,make-rlim) |
1e8b8f9b AM |
1120 | +$(obj)/net_names.h : $(srctree)/include/linux/socket.h \ |
1121 | + $(srctree)/include/linux/net.h \ | |
1122 | + $(src)/Makefile | |
948a1326 | 1123 | + $(call cmd,make-af) |
1e8b8f9b AM |
1124 | + $(call cmd,make-sock) |
1125 | diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c | |
0776672e | 1126 | index 729e595..181d961 100644 |
1e8b8f9b AM |
1127 | --- a/security/apparmor/apparmorfs.c |
1128 | +++ b/security/apparmor/apparmorfs.c | |
0776672e | 1129 | @@ -807,6 +807,7 @@ static struct aa_fs_entry aa_fs_entry_features[] = { |
5882c9d4 | 1130 | AA_FS_DIR("policy", aa_fs_entry_policy), |
1e8b8f9b AM |
1131 | AA_FS_DIR("domain", aa_fs_entry_domain), |
1132 | AA_FS_DIR("file", aa_fs_entry_file), | |
1133 | + AA_FS_DIR("network", aa_fs_entry_network), | |
1134 | AA_FS_FILE_U64("capability", VFS_CAP_FLAGS_MASK), | |
1135 | AA_FS_DIR("rlimit", aa_fs_entry_rlimit), | |
5882c9d4 | 1136 | AA_FS_DIR("caps", aa_fs_entry_caps), |
1e8b8f9b | 1137 | diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h |
c2c0f25c | 1138 | index ba3dfd1..5d3c419 100644 |
1e8b8f9b AM |
1139 | --- a/security/apparmor/include/audit.h |
1140 | +++ b/security/apparmor/include/audit.h | |
c2c0f25c | 1141 | @@ -125,6 +125,10 @@ struct apparmor_audit_data { |
1e8b8f9b | 1142 | u32 denied; |
5882c9d4 | 1143 | kuid_t ouid; |
1e8b8f9b AM |
1144 | } fs; |
1145 | + struct { | |
1146 | + int type, protocol; | |
1147 | + struct sock *sk; | |
1148 | + } net; | |
1149 | }; | |
1150 | }; | |
1151 | ||
fc63ffa9 | 1152 | diff --git a/security/apparmor/include/net.h b/security/apparmor/include/net.h |
ceaf2cfb | 1153 | new file mode 100644 |
1e8b8f9b | 1154 | index 0000000..cb8a121 |
ceaf2cfb | 1155 | --- /dev/null |
fc63ffa9 | 1156 | +++ b/security/apparmor/include/net.h |
1e8b8f9b | 1157 | @@ -0,0 +1,44 @@ |
9474138d AM |
1158 | +/* |
1159 | + * AppArmor security module | |
1160 | + * | |
fc63ffa9 | 1161 | + * This file contains AppArmor network mediation definitions. |
9474138d AM |
1162 | + * |
1163 | + * Copyright (C) 1998-2008 Novell/SUSE | |
1e8b8f9b | 1164 | + * Copyright 2009-2012 Canonical Ltd. |
9474138d AM |
1165 | + * |
1166 | + * This program is free software; you can redistribute it and/or | |
1167 | + * modify it under the terms of the GNU General Public License as | |
1168 | + * published by the Free Software Foundation, version 2 of the | |
1169 | + * License. | |
1170 | + */ | |
1171 | + | |
fc63ffa9 AM |
1172 | +#ifndef __AA_NET_H |
1173 | +#define __AA_NET_H | |
2380c486 | 1174 | + |
fc63ffa9 | 1175 | +#include <net/sock.h> |
9474138d | 1176 | + |
1e8b8f9b AM |
1177 | +#include "apparmorfs.h" |
1178 | + | |
fc63ffa9 AM |
1179 | +/* struct aa_net - network confinement data |
1180 | + * @allowed: basic network families permissions | |
1181 | + * @audit_network: which network permissions to force audit | |
1182 | + * @quiet_network: which network permissions to quiet rejects | |
1183 | + */ | |
1184 | +struct aa_net { | |
1185 | + u16 allow[AF_MAX]; | |
1186 | + u16 audit[AF_MAX]; | |
1187 | + u16 quiet[AF_MAX]; | |
1188 | +}; | |
ceaf2cfb | 1189 | + |
1e8b8f9b AM |
1190 | +extern struct aa_fs_entry aa_fs_entry_network[]; |
1191 | + | |
fc63ffa9 AM |
1192 | +extern int aa_net_perm(int op, struct aa_profile *profile, u16 family, |
1193 | + int type, int protocol, struct sock *sk); | |
1194 | +extern int aa_revalidate_sk(int op, struct sock *sk); | |
ceaf2cfb | 1195 | + |
fc63ffa9 AM |
1196 | +static inline void aa_free_net_rules(struct aa_net *new) |
1197 | +{ | |
1198 | + /* NOP */ | |
ceaf2cfb AM |
1199 | +} |
1200 | + | |
fc63ffa9 AM |
1201 | +#endif /* __AA_NET_H */ |
1202 | diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h | |
0776672e | 1203 | index 52275f0..4fc4dac 100644 |
fc63ffa9 AM |
1204 | --- a/security/apparmor/include/policy.h |
1205 | +++ b/security/apparmor/include/policy.h | |
1206 | @@ -27,6 +27,7 @@ | |
1207 | #include "capability.h" | |
1208 | #include "domain.h" | |
1209 | #include "file.h" | |
1210 | +#include "net.h" | |
1211 | #include "resource.h" | |
1212 | ||
5882c9d4 AM |
1213 | extern const char *const aa_profile_mode_names[]; |
1214 | @@ -176,6 +177,7 @@ struct aa_replacedby { | |
1e8b8f9b | 1215 | * @policy: general match rules governing policy |
fc63ffa9 AM |
1216 | * @file: The set of rules governing basic file access and domain transitions |
1217 | * @caps: capabilities for the profile | |
1218 | + * @net: network controls for the profile | |
1219 | * @rlimits: rlimits for the profile | |
1220 | * | |
5882c9d4 AM |
1221 | * @dents: dentries for the profiles file entries in apparmorfs |
1222 | @@ -217,6 +219,7 @@ struct aa_profile { | |
1e8b8f9b | 1223 | struct aa_policydb policy; |
fc63ffa9 AM |
1224 | struct aa_file_rules file; |
1225 | struct aa_caps caps; | |
1226 | + struct aa_net net; | |
1227 | struct aa_rlimit rlimits; | |
fc63ffa9 | 1228 | |
5882c9d4 | 1229 | unsigned char *hash; |
fc63ffa9 | 1230 | diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c |
0776672e | 1231 | index ba8207b..88d3b0a 100644 |
fc63ffa9 AM |
1232 | --- a/security/apparmor/lsm.c |
1233 | +++ b/security/apparmor/lsm.c | |
948a1326 | 1234 | @@ -32,6 +32,7 @@ |
fc63ffa9 AM |
1235 | #include "include/context.h" |
1236 | #include "include/file.h" | |
1237 | #include "include/ipc.h" | |
1238 | +#include "include/net.h" | |
1239 | #include "include/path.h" | |
1240 | #include "include/policy.h" | |
1241 | #include "include/procattr.h" | |
0776672e | 1242 | @@ -584,6 +585,104 @@ static int apparmor_task_setrlimit(struct task_struct *task, |
fc63ffa9 AM |
1243 | return error; |
1244 | } | |
1245 | ||
1246 | +static int apparmor_socket_create(int family, int type, int protocol, int kern) | |
ceaf2cfb | 1247 | +{ |
fc63ffa9 AM |
1248 | + struct aa_profile *profile; |
1249 | + int error = 0; | |
ceaf2cfb | 1250 | + |
fc63ffa9 AM |
1251 | + if (kern) |
1252 | + return 0; | |
ceaf2cfb | 1253 | + |
fc63ffa9 AM |
1254 | + profile = __aa_current_profile(); |
1255 | + if (!unconfined(profile)) | |
1256 | + error = aa_net_perm(OP_CREATE, profile, family, type, protocol, | |
1257 | + NULL); | |
1258 | + return error; | |
1259 | +} | |
ceaf2cfb | 1260 | + |
fc63ffa9 AM |
1261 | +static int apparmor_socket_bind(struct socket *sock, |
1262 | + struct sockaddr *address, int addrlen) | |
9474138d | 1263 | +{ |
fc63ffa9 | 1264 | + struct sock *sk = sock->sk; |
9474138d | 1265 | + |
fc63ffa9 AM |
1266 | + return aa_revalidate_sk(OP_BIND, sk); |
1267 | +} | |
ceaf2cfb | 1268 | + |
fc63ffa9 AM |
1269 | +static int apparmor_socket_connect(struct socket *sock, |
1270 | + struct sockaddr *address, int addrlen) | |
1271 | +{ | |
1272 | + struct sock *sk = sock->sk; | |
ceaf2cfb | 1273 | + |
fc63ffa9 | 1274 | + return aa_revalidate_sk(OP_CONNECT, sk); |
9474138d AM |
1275 | +} |
1276 | + | |
fc63ffa9 | 1277 | +static int apparmor_socket_listen(struct socket *sock, int backlog) |
9474138d | 1278 | +{ |
fc63ffa9 AM |
1279 | + struct sock *sk = sock->sk; |
1280 | + | |
1281 | + return aa_revalidate_sk(OP_LISTEN, sk); | |
9474138d AM |
1282 | +} |
1283 | + | |
fc63ffa9 | 1284 | +static int apparmor_socket_accept(struct socket *sock, struct socket *newsock) |
9474138d | 1285 | +{ |
fc63ffa9 | 1286 | + struct sock *sk = sock->sk; |
9474138d | 1287 | + |
fc63ffa9 AM |
1288 | + return aa_revalidate_sk(OP_ACCEPT, sk); |
1289 | +} | |
9474138d | 1290 | + |
fc63ffa9 AM |
1291 | +static int apparmor_socket_sendmsg(struct socket *sock, |
1292 | + struct msghdr *msg, int size) | |
76514441 | 1293 | +{ |
fc63ffa9 | 1294 | + struct sock *sk = sock->sk; |
76514441 | 1295 | + |
fc63ffa9 | 1296 | + return aa_revalidate_sk(OP_SENDMSG, sk); |
76514441 AM |
1297 | +} |
1298 | + | |
fc63ffa9 AM |
1299 | +static int apparmor_socket_recvmsg(struct socket *sock, |
1300 | + struct msghdr *msg, int size, int flags) | |
1301 | +{ | |
1302 | + struct sock *sk = sock->sk; | |
ceaf2cfb | 1303 | + |
fc63ffa9 AM |
1304 | + return aa_revalidate_sk(OP_RECVMSG, sk); |
1305 | +} | |
ceaf2cfb | 1306 | + |
fc63ffa9 AM |
1307 | +static int apparmor_socket_getsockname(struct socket *sock) |
1308 | +{ | |
1309 | + struct sock *sk = sock->sk; | |
ceaf2cfb | 1310 | + |
fc63ffa9 | 1311 | + return aa_revalidate_sk(OP_GETSOCKNAME, sk); |
9474138d AM |
1312 | +} |
1313 | + | |
fc63ffa9 | 1314 | +static int apparmor_socket_getpeername(struct socket *sock) |
9474138d | 1315 | +{ |
fc63ffa9 | 1316 | + struct sock *sk = sock->sk; |
9474138d | 1317 | + |
fc63ffa9 AM |
1318 | + return aa_revalidate_sk(OP_GETPEERNAME, sk); |
1319 | +} | |
ceaf2cfb | 1320 | + |
fc63ffa9 AM |
1321 | +static int apparmor_socket_getsockopt(struct socket *sock, int level, |
1322 | + int optname) | |
1323 | +{ | |
1324 | + struct sock *sk = sock->sk; | |
ceaf2cfb | 1325 | + |
fc63ffa9 AM |
1326 | + return aa_revalidate_sk(OP_GETSOCKOPT, sk); |
1327 | +} | |
ceaf2cfb | 1328 | + |
fc63ffa9 AM |
1329 | +static int apparmor_socket_setsockopt(struct socket *sock, int level, |
1330 | + int optname) | |
1331 | +{ | |
1332 | + struct sock *sk = sock->sk; | |
2380c486 | 1333 | + |
fc63ffa9 AM |
1334 | + return aa_revalidate_sk(OP_SETSOCKOPT, sk); |
1335 | +} | |
ceaf2cfb | 1336 | + |
fc63ffa9 AM |
1337 | +static int apparmor_socket_shutdown(struct socket *sock, int how) |
1338 | +{ | |
1339 | + struct sock *sk = sock->sk; | |
ceaf2cfb | 1340 | + |
fc63ffa9 | 1341 | + return aa_revalidate_sk(OP_SOCK_SHUTDOWN, sk); |
ceaf2cfb | 1342 | +} |
948a1326 | 1343 | + |
c2c0f25c AM |
1344 | static struct security_hook_list apparmor_hooks[] = { |
1345 | LSM_HOOK_INIT(ptrace_access_check, apparmor_ptrace_access_check), | |
1346 | LSM_HOOK_INIT(ptrace_traceme, apparmor_ptrace_traceme), | |
0776672e | 1347 | @@ -613,6 +712,19 @@ static struct security_hook_list apparmor_hooks[] = { |
c2c0f25c AM |
1348 | LSM_HOOK_INIT(getprocattr, apparmor_getprocattr), |
1349 | LSM_HOOK_INIT(setprocattr, apparmor_setprocattr), | |
5d26c04f | 1350 | |
c2c0f25c AM |
1351 | + LSM_HOOK_INIT(socket_create, apparmor_socket_create), |
1352 | + LSM_HOOK_INIT(socket_bind, apparmor_socket_bind), | |
1353 | + LSM_HOOK_INIT(socket_connect, apparmor_socket_connect), | |
1354 | + LSM_HOOK_INIT(socket_listen, apparmor_socket_listen), | |
1355 | + LSM_HOOK_INIT(socket_accept, apparmor_socket_accept), | |
1356 | + LSM_HOOK_INIT(socket_sendmsg, apparmor_socket_sendmsg), | |
1357 | + LSM_HOOK_INIT(socket_recvmsg, apparmor_socket_recvmsg), | |
1358 | + LSM_HOOK_INIT(socket_getsockname, apparmor_socket_getsockname), | |
1359 | + LSM_HOOK_INIT(socket_getpeername, apparmor_socket_getpeername), | |
1360 | + LSM_HOOK_INIT(socket_getsockopt, apparmor_socket_getsockopt), | |
1361 | + LSM_HOOK_INIT(socket_setsockopt, apparmor_socket_setsockopt), | |
1362 | + LSM_HOOK_INIT(socket_shutdown, apparmor_socket_shutdown), | |
1363 | + | |
1364 | LSM_HOOK_INIT(cred_alloc_blank, apparmor_cred_alloc_blank), | |
1365 | LSM_HOOK_INIT(cred_free, apparmor_cred_free), | |
1366 | LSM_HOOK_INIT(cred_prepare, apparmor_cred_prepare), | |
fc63ffa9 | 1367 | diff --git a/security/apparmor/net.c b/security/apparmor/net.c |
ceaf2cfb | 1368 | new file mode 100644 |
2dfbb274 | 1369 | index 0000000..003dd18 |
ceaf2cfb | 1370 | --- /dev/null |
fc63ffa9 | 1371 | +++ b/security/apparmor/net.c |
1e8b8f9b | 1372 | @@ -0,0 +1,162 @@ |
2380c486 | 1373 | +/* |
9474138d AM |
1374 | + * AppArmor security module |
1375 | + * | |
fc63ffa9 | 1376 | + * This file contains AppArmor network mediation |
2380c486 | 1377 | + * |
9474138d | 1378 | + * Copyright (C) 1998-2008 Novell/SUSE |
1e8b8f9b | 1379 | + * Copyright 2009-2012 Canonical Ltd. |
2380c486 | 1380 | + * |
9474138d AM |
1381 | + * This program is free software; you can redistribute it and/or |
1382 | + * modify it under the terms of the GNU General Public License as | |
1383 | + * published by the Free Software Foundation, version 2 of the | |
1384 | + * License. | |
2380c486 JR |
1385 | + */ |
1386 | + | |
9474138d AM |
1387 | +#include "include/apparmor.h" |
1388 | +#include "include/audit.h" | |
1389 | +#include "include/context.h" | |
fc63ffa9 | 1390 | +#include "include/net.h" |
9474138d | 1391 | +#include "include/policy.h" |
2380c486 | 1392 | + |
1e8b8f9b AM |
1393 | +#include "net_names.h" |
1394 | + | |
1395 | +struct aa_fs_entry aa_fs_entry_network[] = { | |
1396 | + AA_FS_FILE_STRING("af_mask", AA_FS_AF_MASK), | |
1397 | + { } | |
9474138d AM |
1398 | +}; |
1399 | + | |
fc63ffa9 | 1400 | +/* audit callback for net specific fields */ |
76514441 | 1401 | +static void audit_cb(struct audit_buffer *ab, void *va) |
9474138d | 1402 | +{ |
76514441 | 1403 | + struct common_audit_data *sa = va; |
9474138d | 1404 | + |
fc63ffa9 | 1405 | + audit_log_format(ab, " family="); |
1e8b8f9b AM |
1406 | + if (address_family_names[sa->u.net->family]) { |
1407 | + audit_log_string(ab, address_family_names[sa->u.net->family]); | |
fc63ffa9 | 1408 | + } else { |
1e8b8f9b | 1409 | + audit_log_format(ab, "\"unknown(%d)\"", sa->u.net->family); |
fc63ffa9 | 1410 | + } |
fc63ffa9 | 1411 | + audit_log_format(ab, " sock_type="); |
1e8b8f9b AM |
1412 | + if (sock_type_names[sa->aad->net.type]) { |
1413 | + audit_log_string(ab, sock_type_names[sa->aad->net.type]); | |
fc63ffa9 | 1414 | + } else { |
1e8b8f9b | 1415 | + audit_log_format(ab, "\"unknown(%d)\"", sa->aad->net.type); |
fc63ffa9 | 1416 | + } |
1e8b8f9b | 1417 | + audit_log_format(ab, " protocol=%d", sa->aad->net.protocol); |
2380c486 JR |
1418 | +} |
1419 | + | |
1420 | +/** | |
fc63ffa9 AM |
1421 | + * audit_net - audit network access |
1422 | + * @profile: profile being enforced (NOT NULL) | |
1423 | + * @op: operation being checked | |
1424 | + * @family: network family | |
1425 | + * @type: network type | |
1426 | + * @protocol: network protocol | |
1427 | + * @sk: socket auditing is being applied to | |
1428 | + * @error: error code for failure else 0 | |
ceaf2cfb | 1429 | + * |
fc63ffa9 | 1430 | + * Returns: %0 or sa->error else other errorcode on failure |
2380c486 | 1431 | + */ |
fc63ffa9 AM |
1432 | +static int audit_net(struct aa_profile *profile, int op, u16 family, int type, |
1433 | + int protocol, struct sock *sk, int error) | |
2380c486 | 1434 | +{ |
fc63ffa9 AM |
1435 | + int audit_type = AUDIT_APPARMOR_AUTO; |
1436 | + struct common_audit_data sa; | |
1e8b8f9b AM |
1437 | + struct apparmor_audit_data aad = { }; |
1438 | + struct lsm_network_audit net = { }; | |
fc63ffa9 | 1439 | + if (sk) { |
0c3ec466 | 1440 | + sa.type = LSM_AUDIT_DATA_NET; |
fc63ffa9 | 1441 | + } else { |
0c3ec466 | 1442 | + sa.type = LSM_AUDIT_DATA_NONE; |
2380c486 | 1443 | + } |
fc63ffa9 | 1444 | + /* todo fill in socket addr info */ |
1e8b8f9b AM |
1445 | + sa.aad = &aad; |
1446 | + sa.u.net = &net; | |
1447 | + sa.aad->op = op, | |
1448 | + sa.u.net->family = family; | |
1449 | + sa.u.net->sk = sk; | |
1450 | + sa.aad->net.type = type; | |
1451 | + sa.aad->net.protocol = protocol; | |
1452 | + sa.aad->error = error; | |
1453 | + | |
1454 | + if (likely(!sa.aad->error)) { | |
1455 | + u16 audit_mask = profile->net.audit[sa.u.net->family]; | |
fc63ffa9 | 1456 | + if (likely((AUDIT_MODE(profile) != AUDIT_ALL) && |
1e8b8f9b | 1457 | + !(1 << sa.aad->net.type & audit_mask))) |
ceaf2cfb | 1458 | + return 0; |
fc63ffa9 AM |
1459 | + audit_type = AUDIT_APPARMOR_AUDIT; |
1460 | + } else { | |
1e8b8f9b | 1461 | + u16 quiet_mask = profile->net.quiet[sa.u.net->family]; |
fc63ffa9 | 1462 | + u16 kill_mask = 0; |
1e8b8f9b | 1463 | + u16 denied = (1 << sa.aad->net.type) & ~quiet_mask; |
9474138d | 1464 | + |
fc63ffa9 AM |
1465 | + if (denied & kill_mask) |
1466 | + audit_type = AUDIT_APPARMOR_KILL; | |
9474138d | 1467 | + |
fc63ffa9 AM |
1468 | + if ((denied & quiet_mask) && |
1469 | + AUDIT_MODE(profile) != AUDIT_NOQUIET && | |
1470 | + AUDIT_MODE(profile) != AUDIT_ALL) | |
1e8b8f9b | 1471 | + return COMPLAIN_MODE(profile) ? 0 : sa.aad->error; |
9474138d AM |
1472 | + } |
1473 | + | |
fc63ffa9 | 1474 | + return aa_audit(audit_type, profile, GFP_KERNEL, &sa, audit_cb); |
ceaf2cfb AM |
1475 | +} |
1476 | + | |
2380c486 | 1477 | +/** |
fc63ffa9 AM |
1478 | + * aa_net_perm - very course network access check |
1479 | + * @op: operation being checked | |
1480 | + * @profile: profile being enforced (NOT NULL) | |
1481 | + * @family: network family | |
1482 | + * @type: network type | |
1483 | + * @protocol: network protocol | |
2380c486 | 1484 | + * |
fc63ffa9 | 1485 | + * Returns: %0 else error if permission denied |
2380c486 | 1486 | + */ |
fc63ffa9 AM |
1487 | +int aa_net_perm(int op, struct aa_profile *profile, u16 family, int type, |
1488 | + int protocol, struct sock *sk) | |
2380c486 | 1489 | +{ |
fc63ffa9 AM |
1490 | + u16 family_mask; |
1491 | + int error; | |
ceaf2cfb | 1492 | + |
fc63ffa9 AM |
1493 | + if ((family < 0) || (family >= AF_MAX)) |
1494 | + return -EINVAL; | |
ceaf2cfb | 1495 | + |
fc63ffa9 AM |
1496 | + if ((type < 0) || (type >= SOCK_MAX)) |
1497 | + return -EINVAL; | |
76514441 | 1498 | + |
fc63ffa9 AM |
1499 | + /* unix domain and netlink sockets are handled by ipc */ |
1500 | + if (family == AF_UNIX || family == AF_NETLINK) | |
1501 | + return 0; | |
76514441 | 1502 | + |
fc63ffa9 | 1503 | + family_mask = profile->net.allow[family]; |
2380c486 | 1504 | + |
fc63ffa9 | 1505 | + error = (family_mask & (1 << type)) ? 0 : -EACCES; |
ceaf2cfb | 1506 | + |
fc63ffa9 | 1507 | + return audit_net(profile, op, family, type, protocol, sk, error); |
2380c486 JR |
1508 | +} |
1509 | + | |
76514441 | 1510 | +/** |
fc63ffa9 AM |
1511 | + * aa_revalidate_sk - Revalidate access to a sock |
1512 | + * @op: operation being checked | |
1513 | + * @sk: sock being revalidated (NOT NULL) | |
76514441 | 1514 | + * |
fc63ffa9 | 1515 | + * Returns: %0 else error if permission denied |
76514441 | 1516 | + */ |
fc63ffa9 | 1517 | +int aa_revalidate_sk(int op, struct sock *sk) |
2380c486 | 1518 | +{ |
fc63ffa9 AM |
1519 | + struct aa_profile *profile; |
1520 | + int error = 0; | |
2380c486 | 1521 | + |
fc63ffa9 AM |
1522 | + /* aa_revalidate_sk should not be called from interrupt context |
1523 | + * don't mediate these calls as they are not task related | |
1524 | + */ | |
1525 | + if (in_interrupt()) | |
1526 | + return 0; | |
ceaf2cfb | 1527 | + |
fc63ffa9 AM |
1528 | + profile = __aa_current_profile(); |
1529 | + if (!unconfined(profile)) | |
1530 | + error = aa_net_perm(op, profile, sk->sk_family, sk->sk_type, | |
1531 | + sk->sk_protocol, sk); | |
2380c486 | 1532 | + |
fc63ffa9 | 1533 | + return error; |
2380c486 | 1534 | +} |
fc63ffa9 | 1535 | diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c |
0776672e | 1536 | index 179e68d..f1a8541 100644 |
fc63ffa9 AM |
1537 | --- a/security/apparmor/policy.c |
1538 | +++ b/security/apparmor/policy.c | |
5882c9d4 | 1539 | @@ -603,6 +603,7 @@ void aa_free_profile(struct aa_profile *profile) |
fc63ffa9 AM |
1540 | |
1541 | aa_free_file_rules(&profile->file); | |
1542 | aa_free_cap_rules(&profile->caps); | |
1543 | + aa_free_net_rules(&profile->net); | |
1544 | aa_free_rlimit_rules(&profile->rlimits); | |
1545 | ||
5882c9d4 | 1546 | kzfree(profile->dirname); |
fc63ffa9 | 1547 | diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c |
0776672e | 1548 | index dac2121..0107bc4 100644 |
fc63ffa9 AM |
1549 | --- a/security/apparmor/policy_unpack.c |
1550 | +++ b/security/apparmor/policy_unpack.c | |
1e8b8f9b | 1551 | @@ -193,6 +193,19 @@ fail: |
fc63ffa9 AM |
1552 | return 0; |
1553 | } | |
1554 | ||
1555 | +static bool unpack_u16(struct aa_ext *e, u16 *data, const char *name) | |
2380c486 | 1556 | +{ |
fc63ffa9 AM |
1557 | + if (unpack_nameX(e, AA_U16, name)) { |
1558 | + if (!inbounds(e, sizeof(u16))) | |
1559 | + return 0; | |
1560 | + if (data) | |
1561 | + *data = le16_to_cpu(get_unaligned((u16 *) e->pos)); | |
1562 | + e->pos += sizeof(u16); | |
1563 | + return 1; | |
2380c486 | 1564 | + } |
2380c486 JR |
1565 | + return 0; |
1566 | +} | |
1567 | + | |
fc63ffa9 AM |
1568 | static bool unpack_u32(struct aa_ext *e, u32 *data, const char *name) |
1569 | { | |
1570 | if (unpack_nameX(e, AA_U32, name)) { | |
5882c9d4 | 1571 | @@ -476,6 +489,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) |
fc63ffa9 AM |
1572 | { |
1573 | struct aa_profile *profile = NULL; | |
1574 | const char *name = NULL; | |
2380c486 | 1575 | + size_t size = 0; |
1e8b8f9b | 1576 | int i, error = -EPROTO; |
fc63ffa9 AM |
1577 | kernel_cap_t tmpcap; |
1578 | u32 tmp; | |
5882c9d4 | 1579 | @@ -576,6 +590,38 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) |
fc63ffa9 AM |
1580 | if (!unpack_rlimits(e, profile)) |
1581 | goto fail; | |
1582 | ||
ceaf2cfb | 1583 | + size = unpack_array(e, "net_allowed_af"); |
2380c486 | 1584 | + if (size) { |
2380c486 JR |
1585 | + |
1586 | + for (i = 0; i < size; i++) { | |
fc63ffa9 AM |
1587 | + /* discard extraneous rules that this kernel will |
1588 | + * never request | |
1589 | + */ | |
1efb82ae | 1590 | + if (i >= AF_MAX) { |
fc63ffa9 AM |
1591 | + u16 tmp; |
1592 | + if (!unpack_u16(e, &tmp, NULL) || | |
1593 | + !unpack_u16(e, &tmp, NULL) || | |
1594 | + !unpack_u16(e, &tmp, NULL)) | |
1595 | + goto fail; | |
1596 | + continue; | |
1597 | + } | |
76514441 | 1598 | + if (!unpack_u16(e, &profile->net.allow[i], NULL)) |
2380c486 | 1599 | + goto fail; |
ceaf2cfb | 1600 | + if (!unpack_u16(e, &profile->net.audit[i], NULL)) |
2380c486 | 1601 | + goto fail; |
ceaf2cfb | 1602 | + if (!unpack_u16(e, &profile->net.quiet[i], NULL)) |
2380c486 JR |
1603 | + goto fail; |
1604 | + } | |
ceaf2cfb | 1605 | + if (!unpack_nameX(e, AA_ARRAYEND, NULL)) |
2380c486 | 1606 | + goto fail; |
2380c486 | 1607 | + } |
1e8b8f9b AM |
1608 | + /* |
1609 | + * allow unix domain and netlink sockets they are handled | |
1610 | + * by IPC | |
1611 | + */ | |
76514441 AM |
1612 | + profile->net.allow[AF_UNIX] = 0xffff; |
1613 | + profile->net.allow[AF_NETLINK] = 0xffff; | |
2380c486 | 1614 | + |
1e8b8f9b AM |
1615 | if (unpack_nameX(e, AA_STRUCT, "policydb")) { |
1616 | /* generic policy dfa - optional and may be NULL */ | |
1617 | profile->policy.dfa = unpack_dfa(e); | |
5882c9d4 | 1618 | |
0776672e AM |
1619 | commit 64c5e24470a219c79c2870c63f18f6bd55648b1b |
1620 | Author: John Johansen <john.johansen@canonical.com> | |
1621 | Date: Fri Jun 29 17:34:00 2012 -0700 | |
5882c9d4 | 1622 | |
0776672e AM |
1623 | apparmor: Fix quieting of audit messages for network mediation |
1624 | ||
1625 | If a profile specified a quieting of network denials for a given rule by | |
1626 | either the quiet or deny rule qualifiers, the resultant quiet mask for | |
1627 | denied requests was applied incorrectly, resulting in two potential bugs. | |
1628 | 1. The misapplied quiet mask would prevent denials from being correctly | |
1629 | tested against the kill mask/mode. Thus network access requests that | |
1630 | should have resulted in the application being killed did not. | |
1631 | ||
1632 | 2. The actual quieting of the denied network request was not being applied. | |
1633 | This would result in network rejections always being logged even when | |
1634 | they had been specifically marked as quieted. | |
1635 | ||
1636 | Signed-off-by: John Johansen <john.johansen@canonical.com> | |
5882c9d4 AM |
1637 | |
1638 | diff --git a/security/apparmor/net.c b/security/apparmor/net.c | |
1639 | index 003dd18..6e6e5c9 100644 | |
1640 | --- a/security/apparmor/net.c | |
1641 | +++ b/security/apparmor/net.c | |
1642 | @@ -88,7 +88,7 @@ static int audit_net(struct aa_profile *profile, int op, u16 family, int type, | |
1643 | } else { | |
1644 | u16 quiet_mask = profile->net.quiet[sa.u.net->family]; | |
1645 | u16 kill_mask = 0; | |
1646 | - u16 denied = (1 << sa.aad->net.type) & ~quiet_mask; | |
1647 | + u16 denied = (1 << sa.aad->net.type); | |
1648 | ||
1649 | if (denied & kill_mask) | |
1650 | audit_type = AUDIT_APPARMOR_KILL; | |
5882c9d4 | 1651 | |
0776672e AM |
1652 | commit f7cef61751a2382fb4ea26c18736d7552ffdb24a |
1653 | Author: John Johansen <john.johansen@canonical.com> | |
1654 | Date: Wed May 16 10:58:05 2012 -0700 | |
1e8b8f9b | 1655 | |
0776672e AM |
1656 | UBUNTU: SAUCE: apparmor: Add the ability to mediate mount |
1657 | ||
1658 | Add the ability for apparmor to do mediation of mount operations. Mount | |
1659 | rules require an updated apparmor_parser (2.8 series) for policy compilation. | |
1660 | ||
1661 | The basic form of the rules are. | |
1662 | ||
1663 | [audit] [deny] mount [conds]* [device] [ -> [conds] path], | |
1664 | [audit] [deny] remount [conds]* [path], | |
1665 | [audit] [deny] umount [conds]* [path], | |
1666 | [audit] [deny] pivotroot [oldroot=<value>] <path> | |
1667 | ||
1668 | remount is just a short cut for mount options=remount | |
1669 | ||
1670 | where [conds] can be | |
1671 | fstype=<expr> | |
1672 | options=<expr> | |
1673 | ||
1674 | Example mount commands | |
1675 | mount, # allow all mounts, but not umount or pivotroot | |
1676 | ||
1677 | mount fstype=procfs, # allow mounting procfs anywhere | |
1678 | ||
1679 | mount options=(bind, ro) /foo -> /bar, # readonly bind mount | |
1680 | ||
1681 | mount /dev/sda -> /mnt, | |
1682 | ||
1683 | mount /dev/sd** -> /mnt/**, | |
1684 | ||
1685 | mount fstype=overlayfs options=(rw,upperdir=/tmp/upper/,lowerdir=/) -> /mnt/ | |
1686 | ||
1687 | umount, | |
1688 | ||
1689 | umount /m*, | |
1690 | ||
1691 | See the apparmor userspace for full documentation | |
1692 | ||
1693 | Signed-off-by: John Johansen <john.johansen@canonical.com> | |
1694 | Acked-by: Kees Cook <kees@ubuntu.com> | |
5882c9d4 | 1695 | |
fc63ffa9 | 1696 | diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile |
5882c9d4 | 1697 | index 5dbb72f..89b3445 100644 |
fc63ffa9 AM |
1698 | --- a/security/apparmor/Makefile |
1699 | +++ b/security/apparmor/Makefile | |
1e8b8f9b AM |
1700 | @@ -4,7 +4,7 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o |
1701 | ||
948a1326 | 1702 | apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \ |
fc63ffa9 | 1703 | path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \ |
1e8b8f9b AM |
1704 | - resource.o sid.o file.o net.o |
1705 | + resource.o sid.o file.o net.o mount.o | |
5882c9d4 | 1706 | apparmor-$(CONFIG_SECURITY_APPARMOR_HASH) += crypto.o |
fc63ffa9 | 1707 | |
1e8b8f9b | 1708 | clean-files := capability_names.h rlim_names.h net_names.h |
1e8b8f9b | 1709 | diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c |
0776672e | 1710 | index 181d961..5fb67f6 100644 |
1e8b8f9b AM |
1711 | --- a/security/apparmor/apparmorfs.c |
1712 | +++ b/security/apparmor/apparmorfs.c | |
0776672e | 1713 | @@ -800,7 +800,18 @@ static struct aa_fs_entry aa_fs_entry_domain[] = { |
1e8b8f9b | 1714 | |
5882c9d4 AM |
1715 | static struct aa_fs_entry aa_fs_entry_policy[] = { |
1716 | AA_FS_FILE_BOOLEAN("set_load", 1), | |
1717 | - {} | |
1718 | + { } | |
1719 | +}; | |
1720 | + | |
1e8b8f9b AM |
1721 | +static struct aa_fs_entry aa_fs_entry_mount[] = { |
1722 | + AA_FS_FILE_STRING("mask", "mount umount"), | |
1723 | + { } | |
1724 | +}; | |
1725 | + | |
1726 | +static struct aa_fs_entry aa_fs_entry_namespaces[] = { | |
1727 | + AA_FS_FILE_BOOLEAN("profile", 1), | |
1728 | + AA_FS_FILE_BOOLEAN("pivot_root", 1), | |
1729 | + { } | |
5882c9d4 AM |
1730 | }; |
1731 | ||
1e8b8f9b | 1732 | static struct aa_fs_entry aa_fs_entry_features[] = { |
0776672e | 1733 | @@ -808,6 +819,8 @@ static struct aa_fs_entry aa_fs_entry_features[] = { |
1e8b8f9b AM |
1734 | AA_FS_DIR("domain", aa_fs_entry_domain), |
1735 | AA_FS_DIR("file", aa_fs_entry_file), | |
1736 | AA_FS_DIR("network", aa_fs_entry_network), | |
1737 | + AA_FS_DIR("mount", aa_fs_entry_mount), | |
1738 | + AA_FS_DIR("namespaces", aa_fs_entry_namespaces), | |
1739 | AA_FS_FILE_U64("capability", VFS_CAP_FLAGS_MASK), | |
1740 | AA_FS_DIR("rlimit", aa_fs_entry_rlimit), | |
5882c9d4 | 1741 | AA_FS_DIR("caps", aa_fs_entry_caps), |
1e8b8f9b | 1742 | diff --git a/security/apparmor/audit.c b/security/apparmor/audit.c |
0776672e | 1743 | index 3a7f1da..c2a8b8a 100644 |
1e8b8f9b AM |
1744 | --- a/security/apparmor/audit.c |
1745 | +++ b/security/apparmor/audit.c | |
1746 | @@ -44,6 +44,10 @@ const char *const op_table[] = { | |
1747 | "file_mmap", | |
1748 | "file_mprotect", | |
1749 | ||
1750 | + "pivotroot", | |
1751 | + "mount", | |
1752 | + "umount", | |
1753 | + | |
1754 | "create", | |
1755 | "post_create", | |
1756 | "bind", | |
1757 | diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c | |
0776672e | 1758 | index fc3036b..f2a83b4 100644 |
1e8b8f9b AM |
1759 | --- a/security/apparmor/domain.c |
1760 | +++ b/security/apparmor/domain.c | |
c2c0f25c | 1761 | @@ -236,7 +236,7 @@ static const char *next_name(int xtype, const char *name) |
1e8b8f9b AM |
1762 | * |
1763 | * Returns: refcounted profile, or NULL on failure (MAYBE NULL) | |
1764 | */ | |
1765 | -static struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex) | |
1766 | +struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex) | |
1767 | { | |
1768 | struct aa_profile *new_profile = NULL; | |
1769 | struct aa_namespace *ns = profile->ns; | |
1770 | diff --git a/security/apparmor/include/apparmor.h b/security/apparmor/include/apparmor.h | |
c2c0f25c | 1771 | index e4ea626..ce6ff6a 100644 |
1e8b8f9b AM |
1772 | --- a/security/apparmor/include/apparmor.h |
1773 | +++ b/security/apparmor/include/apparmor.h | |
5882c9d4 | 1774 | @@ -30,8 +30,9 @@ |
1e8b8f9b AM |
1775 | #define AA_CLASS_NET 4 |
1776 | #define AA_CLASS_RLIMITS 5 | |
1777 | #define AA_CLASS_DOMAIN 6 | |
1778 | +#define AA_CLASS_MOUNT 7 | |
1779 | ||
1780 | -#define AA_CLASS_LAST AA_CLASS_DOMAIN | |
1781 | +#define AA_CLASS_LAST AA_CLASS_MOUNT | |
1782 | ||
1783 | /* Control parameters settable through module/boot flags */ | |
1784 | extern enum audit_mode aa_g_audit; | |
1785 | diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h | |
c2c0f25c | 1786 | index 5d3c419..b9f1d57 100644 |
1e8b8f9b AM |
1787 | --- a/security/apparmor/include/audit.h |
1788 | +++ b/security/apparmor/include/audit.h | |
5882c9d4 | 1789 | @@ -72,6 +72,10 @@ enum aa_ops { |
1e8b8f9b AM |
1790 | OP_FMMAP, |
1791 | OP_FMPROT, | |
1792 | ||
1793 | + OP_PIVOTROOT, | |
1794 | + OP_MOUNT, | |
1795 | + OP_UMOUNT, | |
1796 | + | |
1797 | OP_CREATE, | |
1798 | OP_POST_CREATE, | |
1799 | OP_BIND, | |
c2c0f25c | 1800 | @@ -120,6 +124,13 @@ struct apparmor_audit_data { |
1e8b8f9b AM |
1801 | unsigned long max; |
1802 | } rlim; | |
1803 | struct { | |
1804 | + const char *src_name; | |
1805 | + const char *type; | |
1806 | + const char *trans; | |
1807 | + const char *data; | |
1808 | + unsigned long flags; | |
1809 | + } mnt; | |
1810 | + struct { | |
1811 | const char *target; | |
1812 | u32 request; | |
1813 | u32 denied; | |
1814 | diff --git a/security/apparmor/include/domain.h b/security/apparmor/include/domain.h | |
1815 | index de04464..a3f70c5 100644 | |
1816 | --- a/security/apparmor/include/domain.h | |
1817 | +++ b/security/apparmor/include/domain.h | |
1818 | @@ -23,6 +23,8 @@ struct aa_domain { | |
1819 | char **table; | |
1820 | }; | |
1821 | ||
1822 | +struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex); | |
1823 | + | |
1824 | int apparmor_bprm_set_creds(struct linux_binprm *bprm); | |
1825 | int apparmor_bprm_secureexec(struct linux_binprm *bprm); | |
1826 | void apparmor_bprm_committing_creds(struct linux_binprm *bprm); | |
1827 | diff --git a/security/apparmor/include/mount.h b/security/apparmor/include/mount.h | |
ceaf2cfb | 1828 | new file mode 100644 |
0776672e | 1829 | index 0000000..a43b1d6 |
ceaf2cfb | 1830 | --- /dev/null |
1e8b8f9b AM |
1831 | +++ b/security/apparmor/include/mount.h |
1832 | @@ -0,0 +1,54 @@ | |
2380c486 | 1833 | +/* |
9474138d AM |
1834 | + * AppArmor security module |
1835 | + * | |
1e8b8f9b | 1836 | + * This file contains AppArmor file mediation function definitions. |
2380c486 | 1837 | + * |
1e8b8f9b | 1838 | + * Copyright 2012 Canonical Ltd. |
2380c486 | 1839 | + * |
9474138d AM |
1840 | + * This program is free software; you can redistribute it and/or |
1841 | + * modify it under the terms of the GNU General Public License as | |
1842 | + * published by the Free Software Foundation, version 2 of the | |
1843 | + * License. | |
2380c486 JR |
1844 | + */ |
1845 | + | |
1e8b8f9b AM |
1846 | +#ifndef __AA_MOUNT_H |
1847 | +#define __AA_MOUNT_H | |
fc63ffa9 | 1848 | + |
1e8b8f9b AM |
1849 | +#include <linux/fs.h> |
1850 | +#include <linux/path.h> | |
2380c486 | 1851 | + |
1e8b8f9b AM |
1852 | +#include "domain.h" |
1853 | +#include "policy.h" | |
76514441 | 1854 | + |
1e8b8f9b AM |
1855 | +/* mount perms */ |
1856 | +#define AA_MAY_PIVOTROOT 0x01 | |
1857 | +#define AA_MAY_MOUNT 0x02 | |
1858 | +#define AA_MAY_UMOUNT 0x04 | |
1859 | +#define AA_AUDIT_DATA 0x40 | |
1860 | +#define AA_CONT_MATCH 0x40 | |
fc63ffa9 | 1861 | + |
1e8b8f9b | 1862 | +#define AA_MS_IGNORE_MASK (MS_KERNMOUNT | MS_NOSEC | MS_ACTIVE | MS_BORN) |
fc63ffa9 | 1863 | + |
0776672e | 1864 | +int aa_remount(struct aa_profile *profile, const struct path *path, |
1e8b8f9b | 1865 | + unsigned long flags, void *data); |
fc63ffa9 | 1866 | + |
0776672e | 1867 | +int aa_bind_mount(struct aa_profile *profile, const struct path *path, |
1e8b8f9b | 1868 | + const char *old_name, unsigned long flags); |
fc63ffa9 | 1869 | + |
fc63ffa9 | 1870 | + |
0776672e | 1871 | +int aa_mount_change_type(struct aa_profile *profile, const struct path *path, |
1e8b8f9b | 1872 | + unsigned long flags); |
fc63ffa9 | 1873 | + |
0776672e | 1874 | +int aa_move_mount(struct aa_profile *profile, const struct path *path, |
1e8b8f9b AM |
1875 | + const char *old_name); |
1876 | + | |
1877 | +int aa_new_mount(struct aa_profile *profile, const char *dev_name, | |
0776672e | 1878 | + const struct path *path, const char *type, unsigned long flags, |
1e8b8f9b AM |
1879 | + void *data); |
1880 | + | |
1881 | +int aa_umount(struct aa_profile *profile, struct vfsmount *mnt, int flags); | |
1882 | + | |
0776672e AM |
1883 | +int aa_pivotroot(struct aa_profile *profile, const struct path *old_path, |
1884 | + const struct path *new_path); | |
1e8b8f9b AM |
1885 | + |
1886 | +#endif /* __AA_MOUNT_H */ | |
1887 | diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c | |
0776672e | 1888 | index 88d3b0a..432cbd3 100644 |
1e8b8f9b AM |
1889 | --- a/security/apparmor/lsm.c |
1890 | +++ b/security/apparmor/lsm.c | |
1891 | @@ -36,6 +36,7 @@ | |
1892 | #include "include/path.h" | |
1893 | #include "include/policy.h" | |
1894 | #include "include/procattr.h" | |
1895 | +#include "include/mount.h" | |
1896 | ||
1897 | /* Flag indicating whether initialization completed */ | |
1898 | int apparmor_initialized __initdata; | |
0776672e | 1899 | @@ -469,6 +470,61 @@ static int apparmor_file_mprotect(struct vm_area_struct *vma, |
1e8b8f9b AM |
1900 | !(vma->vm_flags & VM_SHARED) ? MAP_PRIVATE : 0); |
1901 | } | |
1902 | ||
0776672e AM |
1903 | +static int apparmor_sb_mount(const char *dev_name, const struct path *path, |
1904 | + const char *type, unsigned long flags, void *data) | |
2380c486 | 1905 | +{ |
1e8b8f9b AM |
1906 | + struct aa_profile *profile; |
1907 | + int error = 0; | |
76514441 | 1908 | + |
1e8b8f9b AM |
1909 | + /* Discard magic */ |
1910 | + if ((flags & MS_MGC_MSK) == MS_MGC_VAL) | |
1911 | + flags &= ~MS_MGC_MSK; | |
2380c486 | 1912 | + |
1e8b8f9b AM |
1913 | + flags &= ~AA_MS_IGNORE_MASK; |
1914 | + | |
1915 | + profile = __aa_current_profile(); | |
1916 | + if (!unconfined(profile)) { | |
1917 | + if (flags & MS_REMOUNT) | |
1918 | + error = aa_remount(profile, path, flags, data); | |
1919 | + else if (flags & MS_BIND) | |
1920 | + error = aa_bind_mount(profile, path, dev_name, flags); | |
1921 | + else if (flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE | | |
1922 | + MS_UNBINDABLE)) | |
1923 | + error = aa_mount_change_type(profile, path, flags); | |
1924 | + else if (flags & MS_MOVE) | |
1925 | + error = aa_move_mount(profile, path, dev_name); | |
1926 | + else | |
1927 | + error = aa_new_mount(profile, dev_name, path, type, | |
1928 | + flags, data); | |
2380c486 | 1929 | + } |
1e8b8f9b AM |
1930 | + return error; |
1931 | +} | |
2380c486 | 1932 | + |
1e8b8f9b AM |
1933 | +static int apparmor_sb_umount(struct vfsmount *mnt, int flags) |
1934 | +{ | |
1935 | + struct aa_profile *profile; | |
1936 | + int error = 0; | |
1937 | + | |
1938 | + profile = __aa_current_profile(); | |
1939 | + if (!unconfined(profile)) | |
1940 | + error = aa_umount(profile, mnt, flags); | |
1941 | + | |
1942 | + return error; | |
2380c486 JR |
1943 | +} |
1944 | + | |
0776672e AM |
1945 | +static int apparmor_sb_pivotroot(const struct path *old_path, |
1946 | + const struct path *new_path) | |
2380c486 | 1947 | +{ |
1e8b8f9b AM |
1948 | + struct aa_profile *profile; |
1949 | + int error = 0; | |
1950 | + | |
1951 | + profile = __aa_current_profile(); | |
1952 | + if (!unconfined(profile)) | |
1953 | + error = aa_pivotroot(profile, old_path, new_path); | |
1954 | + | |
1955 | + return error; | |
2380c486 JR |
1956 | +} |
1957 | + | |
1e8b8f9b AM |
1958 | static int apparmor_getprocattr(struct task_struct *task, char *name, |
1959 | char **value) | |
1960 | { | |
0776672e | 1961 | @@ -689,6 +745,10 @@ static struct security_hook_list apparmor_hooks[] = { |
c2c0f25c AM |
1962 | LSM_HOOK_INIT(capget, apparmor_capget), |
1963 | LSM_HOOK_INIT(capable, apparmor_capable), | |
1e8b8f9b | 1964 | |
c2c0f25c AM |
1965 | + LSM_HOOK_INIT(sb_mount, apparmor_sb_mount), |
1966 | + LSM_HOOK_INIT(sb_umount, apparmor_sb_umount), | |
1967 | + LSM_HOOK_INIT(sb_pivotroot, apparmor_sb_pivotroot), | |
1968 | + | |
1969 | LSM_HOOK_INIT(path_link, apparmor_path_link), | |
1970 | LSM_HOOK_INIT(path_unlink, apparmor_path_unlink), | |
1971 | LSM_HOOK_INIT(path_symlink, apparmor_path_symlink), | |
1e8b8f9b AM |
1972 | diff --git a/security/apparmor/mount.c b/security/apparmor/mount.c |
1973 | new file mode 100644 | |
0776672e | 1974 | index 0000000..9cf9170 |
1e8b8f9b AM |
1975 | --- /dev/null |
1976 | +++ b/security/apparmor/mount.c | |
1977 | @@ -0,0 +1,620 @@ | |
1978 | +/* | |
1979 | + * AppArmor security module | |
fc63ffa9 | 1980 | + * |
1e8b8f9b | 1981 | + * This file contains AppArmor mediation of files |
ceaf2cfb | 1982 | + * |
1e8b8f9b AM |
1983 | + * Copyright (C) 1998-2008 Novell/SUSE |
1984 | + * Copyright 2009-2012 Canonical Ltd. | |
1985 | + * | |
1986 | + * This program is free software; you can redistribute it and/or | |
1987 | + * modify it under the terms of the GNU General Public License as | |
1988 | + * published by the Free Software Foundation, version 2 of the | |
1989 | + * License. | |
ceaf2cfb | 1990 | + */ |
2380c486 | 1991 | + |
1e8b8f9b AM |
1992 | +#include <linux/fs.h> |
1993 | +#include <linux/mount.h> | |
1994 | +#include <linux/namei.h> | |
2380c486 | 1995 | + |
1e8b8f9b AM |
1996 | +#include "include/apparmor.h" |
1997 | +#include "include/audit.h" | |
1998 | +#include "include/context.h" | |
1999 | +#include "include/domain.h" | |
2000 | +#include "include/file.h" | |
2001 | +#include "include/match.h" | |
2002 | +#include "include/mount.h" | |
2003 | +#include "include/path.h" | |
2004 | +#include "include/policy.h" | |
ceaf2cfb | 2005 | + |
2380c486 | 2006 | + |
1e8b8f9b AM |
2007 | +static void audit_mnt_flags(struct audit_buffer *ab, unsigned long flags) |
2008 | +{ | |
2009 | + if (flags & MS_RDONLY) | |
2010 | + audit_log_format(ab, "ro"); | |
2011 | + else | |
2012 | + audit_log_format(ab, "rw"); | |
2013 | + if (flags & MS_NOSUID) | |
2014 | + audit_log_format(ab, ", nosuid"); | |
2015 | + if (flags & MS_NODEV) | |
2016 | + audit_log_format(ab, ", nodev"); | |
2017 | + if (flags & MS_NOEXEC) | |
2018 | + audit_log_format(ab, ", noexec"); | |
2019 | + if (flags & MS_SYNCHRONOUS) | |
2020 | + audit_log_format(ab, ", sync"); | |
2021 | + if (flags & MS_REMOUNT) | |
2022 | + audit_log_format(ab, ", remount"); | |
2023 | + if (flags & MS_MANDLOCK) | |
2024 | + audit_log_format(ab, ", mand"); | |
2025 | + if (flags & MS_DIRSYNC) | |
2026 | + audit_log_format(ab, ", dirsync"); | |
2027 | + if (flags & MS_NOATIME) | |
2028 | + audit_log_format(ab, ", noatime"); | |
2029 | + if (flags & MS_NODIRATIME) | |
2030 | + audit_log_format(ab, ", nodiratime"); | |
2031 | + if (flags & MS_BIND) | |
2032 | + audit_log_format(ab, flags & MS_REC ? ", rbind" : ", bind"); | |
2033 | + if (flags & MS_MOVE) | |
2034 | + audit_log_format(ab, ", move"); | |
2035 | + if (flags & MS_SILENT) | |
2036 | + audit_log_format(ab, ", silent"); | |
2037 | + if (flags & MS_POSIXACL) | |
2038 | + audit_log_format(ab, ", acl"); | |
2039 | + if (flags & MS_UNBINDABLE) | |
2040 | + audit_log_format(ab, flags & MS_REC ? ", runbindable" : | |
2041 | + ", unbindable"); | |
2042 | + if (flags & MS_PRIVATE) | |
2043 | + audit_log_format(ab, flags & MS_REC ? ", rprivate" : | |
2044 | + ", private"); | |
2045 | + if (flags & MS_SLAVE) | |
2046 | + audit_log_format(ab, flags & MS_REC ? ", rslave" : | |
2047 | + ", slave"); | |
2048 | + if (flags & MS_SHARED) | |
2049 | + audit_log_format(ab, flags & MS_REC ? ", rshared" : | |
2050 | + ", shared"); | |
2051 | + if (flags & MS_RELATIME) | |
2052 | + audit_log_format(ab, ", relatime"); | |
2053 | + if (flags & MS_I_VERSION) | |
2054 | + audit_log_format(ab, ", iversion"); | |
2055 | + if (flags & MS_STRICTATIME) | |
2056 | + audit_log_format(ab, ", strictatime"); | |
2057 | + if (flags & MS_NOUSER) | |
2058 | + audit_log_format(ab, ", nouser"); | |
2380c486 JR |
2059 | +} |
2060 | + | |
ceaf2cfb | 2061 | +/** |
1e8b8f9b AM |
2062 | + * audit_cb - call back for mount specific audit fields |
2063 | + * @ab: audit_buffer (NOT NULL) | |
2064 | + * @va: audit struct to audit values of (NOT NULL) | |
ceaf2cfb | 2065 | + */ |
1e8b8f9b | 2066 | +static void audit_cb(struct audit_buffer *ab, void *va) |
2380c486 | 2067 | +{ |
1e8b8f9b | 2068 | + struct common_audit_data *sa = va; |
9474138d | 2069 | + |
1e8b8f9b AM |
2070 | + if (sa->aad->mnt.type) { |
2071 | + audit_log_format(ab, " fstype="); | |
2072 | + audit_log_untrustedstring(ab, sa->aad->mnt.type); | |
2073 | + } | |
2074 | + if (sa->aad->mnt.src_name) { | |
2075 | + audit_log_format(ab, " srcname="); | |
2076 | + audit_log_untrustedstring(ab, sa->aad->mnt.src_name); | |
2077 | + } | |
2078 | + if (sa->aad->mnt.trans) { | |
2079 | + audit_log_format(ab, " trans="); | |
2080 | + audit_log_untrustedstring(ab, sa->aad->mnt.trans); | |
2081 | + } | |
2082 | + if (sa->aad->mnt.flags || sa->aad->op == OP_MOUNT) { | |
2083 | + audit_log_format(ab, " flags=\""); | |
2084 | + audit_mnt_flags(ab, sa->aad->mnt.flags); | |
2085 | + audit_log_format(ab, "\""); | |
2086 | + } | |
2087 | + if (sa->aad->mnt.data) { | |
2088 | + audit_log_format(ab, " options="); | |
2089 | + audit_log_untrustedstring(ab, sa->aad->mnt.data); | |
2090 | + } | |
9474138d | 2091 | +} |
2380c486 | 2092 | + |
fc63ffa9 | 2093 | +/** |
1e8b8f9b AM |
2094 | + * audit_mount - handle the auditing of mount operations |
2095 | + * @profile: the profile being enforced (NOT NULL) | |
2096 | + * @gfp: allocation flags | |
2097 | + * @op: operation being mediated (NOT NULL) | |
2098 | + * @name: name of object being mediated (MAYBE NULL) | |
2099 | + * @src_name: src_name of object being mediated (MAYBE_NULL) | |
2100 | + * @type: type of filesystem (MAYBE_NULL) | |
2101 | + * @trans: name of trans (MAYBE NULL) | |
2102 | + * @flags: filesystem idependent mount flags | |
2103 | + * @data: filesystem mount flags | |
2104 | + * @request: permissions requested | |
2105 | + * @perms: the permissions computed for the request (NOT NULL) | |
2106 | + * @info: extra information message (MAYBE NULL) | |
2107 | + * @error: 0 if operation allowed else failure error code | |
9474138d | 2108 | + * |
1e8b8f9b | 2109 | + * Returns: %0 or error on failure |
76514441 | 2110 | + */ |
1e8b8f9b AM |
2111 | +static int audit_mount(struct aa_profile *profile, gfp_t gfp, int op, |
2112 | + const char *name, const char *src_name, | |
2113 | + const char *type, const char *trans, | |
2114 | + unsigned long flags, const void *data, u32 request, | |
2115 | + struct file_perms *perms, const char *info, int error) | |
2380c486 | 2116 | +{ |
1e8b8f9b | 2117 | + int audit_type = AUDIT_APPARMOR_AUTO; |
2dfbb274 | 2118 | + struct common_audit_data sa = { }; |
1e8b8f9b | 2119 | + struct apparmor_audit_data aad = { }; |
9474138d | 2120 | + |
1e8b8f9b AM |
2121 | + if (likely(!error)) { |
2122 | + u32 mask = perms->audit; | |
9474138d | 2123 | + |
1e8b8f9b AM |
2124 | + if (unlikely(AUDIT_MODE(profile) == AUDIT_ALL)) |
2125 | + mask = 0xffff; | |
76514441 | 2126 | + |
1e8b8f9b AM |
2127 | + /* mask off perms that are not being force audited */ |
2128 | + request &= mask; | |
fc63ffa9 | 2129 | + |
1e8b8f9b AM |
2130 | + if (likely(!request)) |
2131 | + return 0; | |
2132 | + audit_type = AUDIT_APPARMOR_AUDIT; | |
2133 | + } else { | |
2134 | + /* only report permissions that were denied */ | |
2135 | + request = request & ~perms->allow; | |
2136 | + | |
2137 | + if (request & perms->kill) | |
2138 | + audit_type = AUDIT_APPARMOR_KILL; | |
2139 | + | |
2140 | + /* quiet known rejects, assumes quiet and kill do not overlap */ | |
2141 | + if ((request & perms->quiet) && | |
2142 | + AUDIT_MODE(profile) != AUDIT_NOQUIET && | |
2143 | + AUDIT_MODE(profile) != AUDIT_ALL) | |
2144 | + request &= ~perms->quiet; | |
2145 | + | |
2146 | + if (!request) | |
2147 | + return COMPLAIN_MODE(profile) ? | |
2148 | + complain_error(error) : error; | |
2149 | + } | |
2150 | + | |
0c3ec466 | 2151 | + sa.type = LSM_AUDIT_DATA_NONE; |
1e8b8f9b AM |
2152 | + sa.aad = &aad; |
2153 | + sa.aad->op = op; | |
2154 | + sa.aad->name = name; | |
2155 | + sa.aad->mnt.src_name = src_name; | |
2156 | + sa.aad->mnt.type = type; | |
2157 | + sa.aad->mnt.trans = trans; | |
2158 | + sa.aad->mnt.flags = flags; | |
2159 | + if (data && (perms->audit & AA_AUDIT_DATA)) | |
2160 | + sa.aad->mnt.data = data; | |
2161 | + sa.aad->info = info; | |
2162 | + sa.aad->error = error; | |
2163 | + | |
2164 | + return aa_audit(audit_type, profile, gfp, &sa, audit_cb); | |
76514441 AM |
2165 | +} |
2166 | + | |
2167 | +/** | |
1e8b8f9b AM |
2168 | + * match_mnt_flags - Do an ordered match on mount flags |
2169 | + * @dfa: dfa to match against | |
2170 | + * @state: state to start in | |
2171 | + * @flags: mount flags to match against | |
76514441 | 2172 | + * |
1e8b8f9b AM |
2173 | + * Mount flags are encoded as an ordered match. This is done instead of |
2174 | + * checking against a simple bitmask, to allow for logical operations | |
2175 | + * on the flags. | |
76514441 | 2176 | + * |
1e8b8f9b | 2177 | + * Returns: next state after flags match |
76514441 | 2178 | + */ |
1e8b8f9b AM |
2179 | +static unsigned int match_mnt_flags(struct aa_dfa *dfa, unsigned int state, |
2180 | + unsigned long flags) | |
9474138d | 2181 | +{ |
1e8b8f9b | 2182 | + unsigned int i; |
fc63ffa9 | 2183 | + |
1e8b8f9b AM |
2184 | + for (i = 0; i <= 31 ; ++i) { |
2185 | + if ((1 << i) & flags) | |
2186 | + state = aa_dfa_next(dfa, state, i + 1); | |
2187 | + } | |
2188 | + | |
2189 | + return state; | |
9474138d AM |
2190 | +} |
2191 | + | |
2192 | +/** | |
1e8b8f9b AM |
2193 | + * compute_mnt_perms - compute mount permission associated with @state |
2194 | + * @dfa: dfa to match against (NOT NULL) | |
2195 | + * @state: state match finished in | |
ceaf2cfb | 2196 | + * |
1e8b8f9b | 2197 | + * Returns: mount permissions |
9474138d | 2198 | + */ |
1e8b8f9b AM |
2199 | +static struct file_perms compute_mnt_perms(struct aa_dfa *dfa, |
2200 | + unsigned int state) | |
9474138d | 2201 | +{ |
1e8b8f9b | 2202 | + struct file_perms perms; |
2380c486 | 2203 | + |
1e8b8f9b AM |
2204 | + perms.kill = 0; |
2205 | + perms.allow = dfa_user_allow(dfa, state); | |
2206 | + perms.audit = dfa_user_audit(dfa, state); | |
2207 | + perms.quiet = dfa_user_quiet(dfa, state); | |
2208 | + perms.xindex = dfa_user_xindex(dfa, state); | |
2209 | + | |
2210 | + return perms; | |
2211 | +} | |
2212 | + | |
2213 | +static const char const *mnt_info_table[] = { | |
2214 | + "match succeeded", | |
2215 | + "failed mntpnt match", | |
2216 | + "failed srcname match", | |
2217 | + "failed type match", | |
2218 | + "failed flags match", | |
2219 | + "failed data match" | |
2220 | +}; | |
2221 | + | |
2222 | +/* | |
2223 | + * Returns 0 on success else element that match failed in, this is the | |
2224 | + * index into the mnt_info_table above | |
2225 | + */ | |
2226 | +static int do_match_mnt(struct aa_dfa *dfa, unsigned int start, | |
2227 | + const char *mntpnt, const char *devname, | |
2228 | + const char *type, unsigned long flags, | |
2229 | + void *data, bool binary, struct file_perms *perms) | |
2230 | +{ | |
2231 | + unsigned int state; | |
2232 | + | |
2233 | + state = aa_dfa_match(dfa, start, mntpnt); | |
2234 | + state = aa_dfa_null_transition(dfa, state); | |
2235 | + if (!state) | |
2236 | + return 1; | |
2237 | + | |
2238 | + if (devname) | |
2239 | + state = aa_dfa_match(dfa, state, devname); | |
2240 | + state = aa_dfa_null_transition(dfa, state); | |
2241 | + if (!state) | |
2242 | + return 2; | |
2243 | + | |
2244 | + if (type) | |
2245 | + state = aa_dfa_match(dfa, state, type); | |
2246 | + state = aa_dfa_null_transition(dfa, state); | |
2247 | + if (!state) | |
2248 | + return 3; | |
2249 | + | |
2250 | + state = match_mnt_flags(dfa, state, flags); | |
2251 | + if (!state) | |
2252 | + return 4; | |
2253 | + *perms = compute_mnt_perms(dfa, state); | |
2254 | + if (perms->allow & AA_MAY_MOUNT) | |
2255 | + return 0; | |
2256 | + | |
2257 | + /* only match data if not binary and the DFA flags data is expected */ | |
2258 | + if (data && !binary && (perms->allow & AA_CONT_MATCH)) { | |
2259 | + state = aa_dfa_null_transition(dfa, state); | |
2260 | + if (!state) | |
2261 | + return 4; | |
2262 | + | |
2263 | + state = aa_dfa_match(dfa, state, data); | |
2264 | + if (!state) | |
2265 | + return 5; | |
2266 | + *perms = compute_mnt_perms(dfa, state); | |
2267 | + if (perms->allow & AA_MAY_MOUNT) | |
2268 | + return 0; | |
fc63ffa9 | 2269 | + } |
1e8b8f9b AM |
2270 | + |
2271 | + /* failed at end of flags match */ | |
2272 | + return 4; | |
9474138d AM |
2273 | +} |
2274 | + | |
ceaf2cfb | 2275 | +/** |
1e8b8f9b AM |
2276 | + * match_mnt - handle path matching for mount |
2277 | + * @profile: the confining profile | |
2278 | + * @mntpnt: string for the mntpnt (NOT NULL) | |
2279 | + * @devname: string for the devname/src_name (MAYBE NULL) | |
2280 | + * @type: string for the dev type (MAYBE NULL) | |
2281 | + * @flags: mount flags to match | |
2282 | + * @data: fs mount data (MAYBE NULL) | |
2283 | + * @binary: whether @data is binary | |
2284 | + * @perms: Returns: permission found by the match | |
2285 | + * @info: Returns: infomation string about the match for logging | |
fc63ffa9 | 2286 | + * |
1e8b8f9b | 2287 | + * Returns: 0 on success else error |
ceaf2cfb | 2288 | + */ |
1e8b8f9b AM |
2289 | +static int match_mnt(struct aa_profile *profile, const char *mntpnt, |
2290 | + const char *devname, const char *type, | |
2291 | + unsigned long flags, void *data, bool binary, | |
2292 | + struct file_perms *perms, const char **info) | |
9474138d | 2293 | +{ |
1e8b8f9b | 2294 | + int pos; |
2380c486 | 2295 | + |
1e8b8f9b AM |
2296 | + if (!profile->policy.dfa) |
2297 | + return -EACCES; | |
2298 | + | |
2299 | + pos = do_match_mnt(profile->policy.dfa, | |
2300 | + profile->policy.start[AA_CLASS_MOUNT], | |
2301 | + mntpnt, devname, type, flags, data, binary, perms); | |
2302 | + if (pos) { | |
2303 | + *info = mnt_info_table[pos]; | |
2304 | + return -EACCES; | |
2305 | + } | |
2380c486 | 2306 | + |
fc63ffa9 | 2307 | + return 0; |
9474138d | 2308 | +} |
9474138d | 2309 | + |
0776672e | 2310 | +static int path_flags(struct aa_profile *profile, const struct path *path) |
1e8b8f9b AM |
2311 | +{ |
2312 | + return profile->path_flags | | |
2313 | + S_ISDIR(path->dentry->d_inode->i_mode) ? PATH_IS_DIR : 0; | |
2314 | +} | |
9474138d | 2315 | + |
0776672e | 2316 | +int aa_remount(struct aa_profile *profile, const struct path *path, |
1e8b8f9b | 2317 | + unsigned long flags, void *data) |
9474138d | 2318 | +{ |
1e8b8f9b AM |
2319 | + struct file_perms perms = { }; |
2320 | + const char *name, *info = NULL; | |
2321 | + char *buffer = NULL; | |
2322 | + int binary, error; | |
2323 | + | |
2324 | + binary = path->dentry->d_sb->s_type->fs_flags & FS_BINARY_MOUNTDATA; | |
2325 | + | |
2326 | + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, | |
2327 | + &info); | |
2328 | + if (error) | |
2329 | + goto audit; | |
2330 | + | |
2331 | + error = match_mnt(profile, name, NULL, NULL, flags, data, binary, | |
2332 | + &perms, &info); | |
2333 | + | |
2334 | +audit: | |
2335 | + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, NULL, NULL, | |
2336 | + NULL, flags, data, AA_MAY_MOUNT, &perms, info, | |
2337 | + error); | |
2338 | + kfree(buffer); | |
2339 | + | |
2340 | + return error; | |
9474138d AM |
2341 | +} |
2342 | + | |
0776672e | 2343 | +int aa_bind_mount(struct aa_profile *profile, const struct path *path, |
1e8b8f9b | 2344 | + const char *dev_name, unsigned long flags) |
9474138d | 2345 | +{ |
1e8b8f9b AM |
2346 | + struct file_perms perms = { }; |
2347 | + char *buffer = NULL, *old_buffer = NULL; | |
2348 | + const char *name, *old_name = NULL, *info = NULL; | |
2349 | + struct path old_path; | |
2350 | + int error; | |
2351 | + | |
2352 | + if (!dev_name || !*dev_name) | |
2353 | + return -EINVAL; | |
2354 | + | |
2355 | + flags &= MS_REC | MS_BIND; | |
2356 | + | |
2357 | + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, | |
2358 | + &info); | |
2359 | + if (error) | |
2360 | + goto audit; | |
2361 | + | |
2362 | + error = kern_path(dev_name, LOOKUP_FOLLOW|LOOKUP_AUTOMOUNT, &old_path); | |
2363 | + if (error) | |
2364 | + goto audit; | |
2365 | + | |
2366 | + error = aa_path_name(&old_path, path_flags(profile, &old_path), | |
2367 | + &old_buffer, &old_name, &info); | |
2368 | + path_put(&old_path); | |
2369 | + if (error) | |
2370 | + goto audit; | |
2371 | + | |
2372 | + error = match_mnt(profile, name, old_name, NULL, flags, NULL, 0, | |
2373 | + &perms, &info); | |
2374 | + | |
2375 | +audit: | |
2376 | + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, old_name, | |
2377 | + NULL, NULL, flags, NULL, AA_MAY_MOUNT, &perms, | |
2378 | + info, error); | |
2379 | + kfree(buffer); | |
2380 | + kfree(old_buffer); | |
2381 | + | |
2382 | + return error; | |
9474138d | 2383 | +} |
fc63ffa9 | 2384 | + |
0776672e | 2385 | +int aa_mount_change_type(struct aa_profile *profile, const struct path *path, |
1e8b8f9b AM |
2386 | + unsigned long flags) |
2387 | +{ | |
2388 | + struct file_perms perms = { }; | |
2389 | + char *buffer = NULL; | |
2390 | + const char *name, *info = NULL; | |
2391 | + int error; | |
2392 | + | |
2393 | + /* These are the flags allowed by do_change_type() */ | |
2394 | + flags &= (MS_REC | MS_SILENT | MS_SHARED | MS_PRIVATE | MS_SLAVE | | |
2395 | + MS_UNBINDABLE); | |
2396 | + | |
2397 | + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, | |
2398 | + &info); | |
fc63ffa9 | 2399 | + if (error) |
1e8b8f9b AM |
2400 | + goto audit; |
2401 | + | |
2402 | + error = match_mnt(profile, name, NULL, NULL, flags, NULL, 0, &perms, | |
2403 | + &info); | |
2404 | + | |
2405 | +audit: | |
2406 | + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, NULL, NULL, | |
2407 | + NULL, flags, NULL, AA_MAY_MOUNT, &perms, info, | |
2408 | + error); | |
2409 | + kfree(buffer); | |
2410 | + | |
2411 | + return error; | |
2412 | +} | |
2413 | + | |
0776672e | 2414 | +int aa_move_mount(struct aa_profile *profile, const struct path *path, |
1e8b8f9b AM |
2415 | + const char *orig_name) |
2416 | +{ | |
2417 | + struct file_perms perms = { }; | |
2418 | + char *buffer = NULL, *old_buffer = NULL; | |
2419 | + const char *name, *old_name = NULL, *info = NULL; | |
2420 | + struct path old_path; | |
2421 | + int error; | |
2422 | + | |
2423 | + if (!orig_name || !*orig_name) | |
2424 | + return -EINVAL; | |
2425 | + | |
2426 | + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, | |
2427 | + &info); | |
fc63ffa9 | 2428 | + if (error) |
1e8b8f9b AM |
2429 | + goto audit; |
2430 | + | |
2431 | + error = kern_path(orig_name, LOOKUP_FOLLOW, &old_path); | |
fc63ffa9 | 2432 | + if (error) |
1e8b8f9b AM |
2433 | + goto audit; |
2434 | + | |
2435 | + error = aa_path_name(&old_path, path_flags(profile, &old_path), | |
2436 | + &old_buffer, &old_name, &info); | |
2437 | + path_put(&old_path); | |
2438 | + if (error) | |
2439 | + goto audit; | |
2440 | + | |
2441 | + error = match_mnt(profile, name, old_name, NULL, MS_MOVE, NULL, 0, | |
2442 | + &perms, &info); | |
2443 | + | |
2444 | +audit: | |
2445 | + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, old_name, | |
2446 | + NULL, NULL, MS_MOVE, NULL, AA_MAY_MOUNT, &perms, | |
2447 | + info, error); | |
2448 | + kfree(buffer); | |
2449 | + kfree(old_buffer); | |
2450 | + | |
2451 | + return error; | |
2452 | +} | |
2453 | + | |
2454 | +int aa_new_mount(struct aa_profile *profile, const char *orig_dev_name, | |
0776672e | 2455 | + const struct path *path, const char *type, unsigned long flags, |
1e8b8f9b AM |
2456 | + void *data) |
2457 | +{ | |
2458 | + struct file_perms perms = { }; | |
2459 | + char *buffer = NULL, *dev_buffer = NULL; | |
2460 | + const char *name = NULL, *dev_name = NULL, *info = NULL; | |
2461 | + int binary = 1; | |
2462 | + int error; | |
2463 | + | |
2464 | + dev_name = orig_dev_name; | |
2465 | + if (type) { | |
2466 | + int requires_dev; | |
2467 | + struct file_system_type *fstype = get_fs_type(type); | |
2468 | + if (!fstype) | |
2469 | + return -ENODEV; | |
2470 | + | |
2471 | + binary = fstype->fs_flags & FS_BINARY_MOUNTDATA; | |
2472 | + requires_dev = fstype->fs_flags & FS_REQUIRES_DEV; | |
2473 | + put_filesystem(fstype); | |
2474 | + | |
2475 | + if (requires_dev) { | |
2476 | + struct path dev_path; | |
2477 | + | |
2478 | + if (!dev_name || !*dev_name) { | |
2479 | + error = -ENOENT; | |
2480 | + goto out; | |
2481 | + } | |
2482 | + | |
2483 | + error = kern_path(dev_name, LOOKUP_FOLLOW, &dev_path); | |
2484 | + if (error) | |
2485 | + goto audit; | |
2486 | + | |
2487 | + error = aa_path_name(&dev_path, | |
2488 | + path_flags(profile, &dev_path), | |
2489 | + &dev_buffer, &dev_name, &info); | |
2490 | + path_put(&dev_path); | |
2491 | + if (error) | |
2492 | + goto audit; | |
2493 | + } | |
2494 | + } | |
2495 | + | |
2496 | + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, | |
2497 | + &info); | |
2498 | + if (error) | |
2499 | + goto audit; | |
2500 | + | |
2501 | + error = match_mnt(profile, name, dev_name, type, flags, data, binary, | |
2502 | + &perms, &info); | |
2503 | + | |
2504 | +audit: | |
2505 | + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, dev_name, | |
2506 | + type, NULL, flags, data, AA_MAY_MOUNT, &perms, info, | |
2507 | + error); | |
2508 | + kfree(buffer); | |
2509 | + kfree(dev_buffer); | |
2510 | + | |
2511 | +out: | |
2512 | + return error; | |
2513 | + | |
2514 | +} | |
2515 | + | |
2516 | +int aa_umount(struct aa_profile *profile, struct vfsmount *mnt, int flags) | |
2517 | +{ | |
2518 | + struct file_perms perms = { }; | |
2519 | + char *buffer = NULL; | |
2520 | + const char *name, *info = NULL; | |
2521 | + int error; | |
2522 | + | |
2523 | + struct path path = { mnt, mnt->mnt_root }; | |
2524 | + error = aa_path_name(&path, path_flags(profile, &path), &buffer, &name, | |
2525 | + &info); | |
2526 | + if (error) | |
2527 | + goto audit; | |
2528 | + | |
2529 | + if (!error && profile->policy.dfa) { | |
2530 | + unsigned int state; | |
2531 | + state = aa_dfa_match(profile->policy.dfa, | |
2532 | + profile->policy.start[AA_CLASS_MOUNT], | |
2533 | + name); | |
2534 | + perms = compute_mnt_perms(profile->policy.dfa, state); | |
2535 | + } | |
2536 | + | |
2537 | + if (AA_MAY_UMOUNT & ~perms.allow) | |
2538 | + error = -EACCES; | |
2539 | + | |
2540 | +audit: | |
2541 | + error = audit_mount(profile, GFP_KERNEL, OP_UMOUNT, name, NULL, NULL, | |
2542 | + NULL, 0, NULL, AA_MAY_UMOUNT, &perms, info, error); | |
2543 | + kfree(buffer); | |
2544 | + | |
2545 | + return error; | |
2546 | +} | |
2547 | + | |
0776672e AM |
2548 | +int aa_pivotroot(struct aa_profile *profile, const struct path *old_path, |
2549 | + const struct path *new_path) | |
1e8b8f9b AM |
2550 | +{ |
2551 | + struct file_perms perms = { }; | |
2552 | + struct aa_profile *target = NULL; | |
2553 | + char *old_buffer = NULL, *new_buffer = NULL; | |
2554 | + const char *old_name, *new_name = NULL, *info = NULL; | |
2555 | + int error; | |
2556 | + | |
2557 | + error = aa_path_name(old_path, path_flags(profile, old_path), | |
2558 | + &old_buffer, &old_name, &info); | |
2559 | + if (error) | |
2560 | + goto audit; | |
2561 | + | |
2562 | + error = aa_path_name(new_path, path_flags(profile, new_path), | |
2563 | + &new_buffer, &new_name, &info); | |
2564 | + if (error) | |
2565 | + goto audit; | |
2566 | + | |
2567 | + if (profile->policy.dfa) { | |
2568 | + unsigned int state; | |
2569 | + state = aa_dfa_match(profile->policy.dfa, | |
2570 | + profile->policy.start[AA_CLASS_MOUNT], | |
2571 | + new_name); | |
2572 | + state = aa_dfa_null_transition(profile->policy.dfa, state); | |
2573 | + state = aa_dfa_match(profile->policy.dfa, state, old_name); | |
2574 | + perms = compute_mnt_perms(profile->policy.dfa, state); | |
2575 | + } | |
2576 | + | |
2577 | + if (AA_MAY_PIVOTROOT & perms.allow) { | |
2578 | + if ((perms.xindex & AA_X_TYPE_MASK) == AA_X_TABLE) { | |
2579 | + target = x_table_lookup(profile, perms.xindex); | |
2580 | + if (!target) | |
2581 | + error = -ENOENT; | |
2582 | + else | |
2583 | + error = aa_replace_current_profile(target); | |
2584 | + } | |
2585 | + } else | |
2586 | + error = -EACCES; | |
2587 | + | |
2588 | +audit: | |
2589 | + error = audit_mount(profile, GFP_KERNEL, OP_PIVOTROOT, new_name, | |
2590 | + old_name, NULL, target ? target->base.name : NULL, | |
2591 | + 0, NULL, AA_MAY_PIVOTROOT, &perms, info, error); | |
2592 | + aa_put_profile(target); | |
2593 | + kfree(old_buffer); | |
2594 | + kfree(new_buffer); | |
2595 | + | |
2596 | + return error; | |
2597 | +} |