]>
Commit | Line | Data |
---|---|---|
c3701be9 DM |
1 | From 7a445944525ad3b2a3f292ddf0d491ae6ed947c1 Mon Sep 17 00:00:00 2001 |
2 | From: John Johansen <john.johansen@canonical.com> | |
3 | Date: Wed, 16 May 2012 10:58:05 -0700 | |
4 | Subject: [PATCH 4/4] UBUNTU: SAUCE: apparmor: Add the ability to mediate mount | |
5 | ||
6 | Add the ability for apparmor to do mediation of mount operations. Mount | |
7 | rules require an updated apparmor_parser (2.8 series) for policy compilation. | |
8 | ||
9 | The basic form of the rules are. | |
10 | ||
11 | [audit] [deny] mount [conds]* [device] [ -> [conds] path], | |
12 | [audit] [deny] remount [conds]* [path], | |
13 | [audit] [deny] umount [conds]* [path], | |
14 | [audit] [deny] pivotroot [oldroot=<value>] <path> | |
15 | ||
16 | remount is just a short cut for mount options=remount | |
17 | ||
18 | where [conds] can be | |
19 | fstype=<expr> | |
20 | options=<expr> | |
21 | ||
22 | Example mount commands | |
23 | mount, # allow all mounts, but not umount or pivotroot | |
24 | ||
25 | mount fstype=procfs, # allow mounting procfs anywhere | |
26 | ||
27 | mount options=(bind, ro) /foo -> /bar, # readonly bind mount | |
28 | ||
29 | mount /dev/sda -> /mnt, | |
30 | ||
31 | mount /dev/sd** -> /mnt/**, | |
32 | ||
33 | mount fstype=overlayfs options=(rw,upperdir=/tmp/upper/,lowerdir=/) -> /mnt/ | |
34 | ||
35 | umount, | |
36 | ||
37 | umount /m*, | |
38 | ||
39 | See the apparmor userspace for full documentation | |
40 | ||
41 | Signed-off-by: John Johansen <john.johansen@canonical.com> | |
42 | Acked-by: Kees Cook <kees@ubuntu.com> | |
43 | --- | |
44 | security/apparmor/Makefile | 2 +- | |
45 | security/apparmor/apparmorfs.c | 13 + | |
46 | security/apparmor/audit.c | 4 + | |
47 | security/apparmor/domain.c | 2 +- | |
48 | security/apparmor/include/apparmor.h | 3 +- | |
49 | security/apparmor/include/audit.h | 11 + | |
50 | security/apparmor/include/domain.h | 2 + | |
51 | security/apparmor/include/mount.h | 54 +++ | |
52 | security/apparmor/lsm.c | 59 ++++ | |
53 | security/apparmor/mount.c | 620 +++++++++++++++++++++++++++++++++++ | |
54 | 10 files changed, 767 insertions(+), 3 deletions(-) | |
55 | create mode 100644 security/apparmor/include/mount.h | |
56 | create mode 100644 security/apparmor/mount.c | |
57 | ||
58 | diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile | |
59 | index e270692..9b44e1a 100644 | |
60 | --- a/security/apparmor/Makefile | |
61 | +++ b/security/apparmor/Makefile | |
62 | @@ -4,7 +4,7 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o | |
63 | ||
64 | apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \ | |
65 | path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \ | |
66 | - resource.o sid.o file.o net.o | |
67 | + resource.o sid.o file.o net.o mount.o | |
68 | ||
69 | clean-files := capability_names.h rlim_names.h net_names.h | |
70 | ||
71 | diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c | |
72 | index 114fb23..ee77ec9 100644 | |
73 | --- a/security/apparmor/apparmorfs.c | |
74 | +++ b/security/apparmor/apparmorfs.c | |
75 | @@ -426,10 +426,23 @@ static struct aa_fs_entry aa_fs_entry_domain[] = { | |
76 | { } | |
77 | }; | |
78 | ||
79 | +static struct aa_fs_entry aa_fs_entry_mount[] = { | |
80 | + AA_FS_FILE_STRING("mask", "mount umount"), | |
81 | + { } | |
82 | +}; | |
83 | + | |
84 | +static struct aa_fs_entry aa_fs_entry_namespaces[] = { | |
85 | + AA_FS_FILE_BOOLEAN("profile", 1), | |
86 | + AA_FS_FILE_BOOLEAN("pivot_root", 1), | |
87 | + { } | |
88 | +}; | |
89 | + | |
90 | static struct aa_fs_entry aa_fs_entry_features[] = { | |
91 | AA_FS_DIR("domain", aa_fs_entry_domain), | |
92 | AA_FS_DIR("file", aa_fs_entry_file), | |
93 | AA_FS_DIR("network", aa_fs_entry_network), | |
94 | + AA_FS_DIR("mount", aa_fs_entry_mount), | |
95 | + AA_FS_DIR("namespaces", aa_fs_entry_namespaces), | |
96 | AA_FS_FILE_U64("capability", VFS_CAP_FLAGS_MASK), | |
97 | AA_FS_DIR("rlimit", aa_fs_entry_rlimit), | |
98 | { } | |
99 | diff --git a/security/apparmor/audit.c b/security/apparmor/audit.c | |
100 | index 3ae28db..e267963 100644 | |
101 | --- a/security/apparmor/audit.c | |
102 | +++ b/security/apparmor/audit.c | |
103 | @@ -44,6 +44,10 @@ const char *const op_table[] = { | |
104 | "file_mmap", | |
105 | "file_mprotect", | |
106 | ||
107 | + "pivotroot", | |
108 | + "mount", | |
109 | + "umount", | |
110 | + | |
111 | "create", | |
112 | "post_create", | |
113 | "bind", | |
114 | diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c | |
115 | index 859abda..3fee1fe 100644 | |
116 | --- a/security/apparmor/domain.c | |
117 | +++ b/security/apparmor/domain.c | |
118 | @@ -242,7 +242,7 @@ static const char *next_name(int xtype, const char *name) | |
119 | * | |
120 | * Returns: refcounted profile, or NULL on failure (MAYBE NULL) | |
121 | */ | |
122 | -static struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex) | |
123 | +struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex) | |
124 | { | |
125 | struct aa_profile *new_profile = NULL; | |
126 | struct aa_namespace *ns = profile->ns; | |
127 | diff --git a/security/apparmor/include/apparmor.h b/security/apparmor/include/apparmor.h | |
128 | index 40aedd9..e243d96 100644 | |
129 | --- a/security/apparmor/include/apparmor.h | |
130 | +++ b/security/apparmor/include/apparmor.h | |
131 | @@ -29,8 +29,9 @@ | |
132 | #define AA_CLASS_NET 4 | |
133 | #define AA_CLASS_RLIMITS 5 | |
134 | #define AA_CLASS_DOMAIN 6 | |
135 | +#define AA_CLASS_MOUNT 7 | |
136 | ||
137 | -#define AA_CLASS_LAST AA_CLASS_DOMAIN | |
138 | +#define AA_CLASS_LAST AA_CLASS_MOUNT | |
139 | ||
140 | /* Control parameters settable through module/boot flags */ | |
141 | extern enum audit_mode aa_g_audit; | |
142 | diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h | |
143 | index 4af6523..ada004d 100644 | |
144 | --- a/security/apparmor/include/audit.h | |
145 | +++ b/security/apparmor/include/audit.h | |
146 | @@ -73,6 +73,10 @@ enum aa_ops { | |
147 | OP_FMMAP, | |
148 | OP_FMPROT, | |
149 | ||
150 | + OP_PIVOTROOT, | |
151 | + OP_MOUNT, | |
152 | + OP_UMOUNT, | |
153 | + | |
154 | OP_CREATE, | |
155 | OP_POST_CREATE, | |
156 | OP_BIND, | |
157 | @@ -122,6 +126,13 @@ struct apparmor_audit_data { | |
158 | unsigned long max; | |
159 | } rlim; | |
160 | struct { | |
161 | + const char *src_name; | |
162 | + const char *type; | |
163 | + const char *trans; | |
164 | + const char *data; | |
165 | + unsigned long flags; | |
166 | + } mnt; | |
167 | + struct { | |
168 | const char *target; | |
169 | u32 request; | |
170 | u32 denied; | |
171 | diff --git a/security/apparmor/include/domain.h b/security/apparmor/include/domain.h | |
172 | index de04464..a3f70c5 100644 | |
173 | --- a/security/apparmor/include/domain.h | |
174 | +++ b/security/apparmor/include/domain.h | |
175 | @@ -23,6 +23,8 @@ struct aa_domain { | |
176 | char **table; | |
177 | }; | |
178 | ||
179 | +struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex); | |
180 | + | |
181 | int apparmor_bprm_set_creds(struct linux_binprm *bprm); | |
182 | int apparmor_bprm_secureexec(struct linux_binprm *bprm); | |
183 | void apparmor_bprm_committing_creds(struct linux_binprm *bprm); | |
184 | diff --git a/security/apparmor/include/mount.h b/security/apparmor/include/mount.h | |
185 | new file mode 100644 | |
186 | index 0000000..bc17a53 | |
187 | --- /dev/null | |
188 | +++ b/security/apparmor/include/mount.h | |
189 | @@ -0,0 +1,54 @@ | |
190 | +/* | |
191 | + * AppArmor security module | |
192 | + * | |
193 | + * This file contains AppArmor file mediation function definitions. | |
194 | + * | |
195 | + * Copyright 2012 Canonical Ltd. | |
196 | + * | |
197 | + * This program is free software; you can redistribute it and/or | |
198 | + * modify it under the terms of the GNU General Public License as | |
199 | + * published by the Free Software Foundation, version 2 of the | |
200 | + * License. | |
201 | + */ | |
202 | + | |
203 | +#ifndef __AA_MOUNT_H | |
204 | +#define __AA_MOUNT_H | |
205 | + | |
206 | +#include <linux/fs.h> | |
207 | +#include <linux/path.h> | |
208 | + | |
209 | +#include "domain.h" | |
210 | +#include "policy.h" | |
211 | + | |
212 | +/* mount perms */ | |
213 | +#define AA_MAY_PIVOTROOT 0x01 | |
214 | +#define AA_MAY_MOUNT 0x02 | |
215 | +#define AA_MAY_UMOUNT 0x04 | |
216 | +#define AA_AUDIT_DATA 0x40 | |
217 | +#define AA_CONT_MATCH 0x40 | |
218 | + | |
219 | +#define AA_MS_IGNORE_MASK (MS_KERNMOUNT | MS_NOSEC | MS_ACTIVE | MS_BORN) | |
220 | + | |
221 | +int aa_remount(struct aa_profile *profile, struct path *path, | |
222 | + unsigned long flags, void *data); | |
223 | + | |
224 | +int aa_bind_mount(struct aa_profile *profile, struct path *path, | |
225 | + const char *old_name, unsigned long flags); | |
226 | + | |
227 | + | |
228 | +int aa_mount_change_type(struct aa_profile *profile, struct path *path, | |
229 | + unsigned long flags); | |
230 | + | |
231 | +int aa_move_mount(struct aa_profile *profile, struct path *path, | |
232 | + const char *old_name); | |
233 | + | |
234 | +int aa_new_mount(struct aa_profile *profile, const char *dev_name, | |
235 | + struct path *path, const char *type, unsigned long flags, | |
236 | + void *data); | |
237 | + | |
238 | +int aa_umount(struct aa_profile *profile, struct vfsmount *mnt, int flags); | |
239 | + | |
240 | +int aa_pivotroot(struct aa_profile *profile, struct path *old_path, | |
241 | + struct path *new_path); | |
242 | + | |
243 | +#endif /* __AA_MOUNT_H */ | |
244 | diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c | |
245 | index 1bce440..6750673 100644 | |
246 | --- a/security/apparmor/lsm.c | |
247 | +++ b/security/apparmor/lsm.c | |
248 | @@ -36,6 +36,7 @@ | |
249 | #include "include/path.h" | |
250 | #include "include/policy.h" | |
251 | #include "include/procattr.h" | |
252 | +#include "include/mount.h" | |
253 | ||
254 | /* Flag indicating whether initialization completed */ | |
255 | int apparmor_initialized __initdata; | |
256 | @@ -504,6 +505,60 @@ static int apparmor_file_mprotect(struct vm_area_struct *vma, | |
257 | !(vma->vm_flags & VM_SHARED) ? MAP_PRIVATE : 0); | |
258 | } | |
259 | ||
260 | +static int apparmor_sb_mount(char *dev_name, struct path *path, char *type, | |
261 | + unsigned long flags, void *data) | |
262 | +{ | |
263 | + struct aa_profile *profile; | |
264 | + int error = 0; | |
265 | + | |
266 | + /* Discard magic */ | |
267 | + if ((flags & MS_MGC_MSK) == MS_MGC_VAL) | |
268 | + flags &= ~MS_MGC_MSK; | |
269 | + | |
270 | + flags &= ~AA_MS_IGNORE_MASK; | |
271 | + | |
272 | + profile = __aa_current_profile(); | |
273 | + if (!unconfined(profile)) { | |
274 | + if (flags & MS_REMOUNT) | |
275 | + error = aa_remount(profile, path, flags, data); | |
276 | + else if (flags & MS_BIND) | |
277 | + error = aa_bind_mount(profile, path, dev_name, flags); | |
278 | + else if (flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE | | |
279 | + MS_UNBINDABLE)) | |
280 | + error = aa_mount_change_type(profile, path, flags); | |
281 | + else if (flags & MS_MOVE) | |
282 | + error = aa_move_mount(profile, path, dev_name); | |
283 | + else | |
284 | + error = aa_new_mount(profile, dev_name, path, type, | |
285 | + flags, data); | |
286 | + } | |
287 | + return error; | |
288 | +} | |
289 | + | |
290 | +static int apparmor_sb_umount(struct vfsmount *mnt, int flags) | |
291 | +{ | |
292 | + struct aa_profile *profile; | |
293 | + int error = 0; | |
294 | + | |
295 | + profile = __aa_current_profile(); | |
296 | + if (!unconfined(profile)) | |
297 | + error = aa_umount(profile, mnt, flags); | |
298 | + | |
299 | + return error; | |
300 | +} | |
301 | + | |
302 | +static int apparmor_sb_pivotroot(struct path *old_path, struct path *new_path) | |
303 | +{ | |
304 | + struct aa_profile *profile; | |
305 | + int error = 0; | |
306 | + | |
307 | + profile = __aa_current_profile(); | |
308 | + if (!unconfined(profile)) | |
309 | + error = aa_pivotroot(profile, old_path, new_path); | |
310 | + | |
311 | + return error; | |
312 | +} | |
313 | + | |
314 | static int apparmor_getprocattr(struct task_struct *task, char *name, | |
315 | char **value) | |
316 | { | |
317 | @@ -721,6 +776,10 @@ static struct security_operations apparmor_ops = { | |
318 | .capget = apparmor_capget, | |
319 | .capable = apparmor_capable, | |
320 | ||
321 | + .sb_mount = apparmor_sb_mount, | |
322 | + .sb_umount = apparmor_sb_umount, | |
323 | + .sb_pivotroot = apparmor_sb_pivotroot, | |
324 | + | |
325 | .path_link = apparmor_path_link, | |
326 | .path_unlink = apparmor_path_unlink, | |
327 | .path_symlink = apparmor_path_symlink, | |
328 | diff --git a/security/apparmor/mount.c b/security/apparmor/mount.c | |
329 | new file mode 100644 | |
330 | index 0000000..478aa4d | |
331 | --- /dev/null | |
332 | +++ b/security/apparmor/mount.c | |
333 | @@ -0,0 +1,620 @@ | |
334 | +/* | |
335 | + * AppArmor security module | |
336 | + * | |
337 | + * This file contains AppArmor mediation of files | |
338 | + * | |
339 | + * Copyright (C) 1998-2008 Novell/SUSE | |
340 | + * Copyright 2009-2012 Canonical Ltd. | |
341 | + * | |
342 | + * This program is free software; you can redistribute it and/or | |
343 | + * modify it under the terms of the GNU General Public License as | |
344 | + * published by the Free Software Foundation, version 2 of the | |
345 | + * License. | |
346 | + */ | |
347 | + | |
348 | +#include <linux/fs.h> | |
349 | +#include <linux/mount.h> | |
350 | +#include <linux/namei.h> | |
351 | + | |
352 | +#include "include/apparmor.h" | |
353 | +#include "include/audit.h" | |
354 | +#include "include/context.h" | |
355 | +#include "include/domain.h" | |
356 | +#include "include/file.h" | |
357 | +#include "include/match.h" | |
358 | +#include "include/mount.h" | |
359 | +#include "include/path.h" | |
360 | +#include "include/policy.h" | |
361 | + | |
362 | + | |
363 | +static void audit_mnt_flags(struct audit_buffer *ab, unsigned long flags) | |
364 | +{ | |
365 | + if (flags & MS_RDONLY) | |
366 | + audit_log_format(ab, "ro"); | |
367 | + else | |
368 | + audit_log_format(ab, "rw"); | |
369 | + if (flags & MS_NOSUID) | |
370 | + audit_log_format(ab, ", nosuid"); | |
371 | + if (flags & MS_NODEV) | |
372 | + audit_log_format(ab, ", nodev"); | |
373 | + if (flags & MS_NOEXEC) | |
374 | + audit_log_format(ab, ", noexec"); | |
375 | + if (flags & MS_SYNCHRONOUS) | |
376 | + audit_log_format(ab, ", sync"); | |
377 | + if (flags & MS_REMOUNT) | |
378 | + audit_log_format(ab, ", remount"); | |
379 | + if (flags & MS_MANDLOCK) | |
380 | + audit_log_format(ab, ", mand"); | |
381 | + if (flags & MS_DIRSYNC) | |
382 | + audit_log_format(ab, ", dirsync"); | |
383 | + if (flags & MS_NOATIME) | |
384 | + audit_log_format(ab, ", noatime"); | |
385 | + if (flags & MS_NODIRATIME) | |
386 | + audit_log_format(ab, ", nodiratime"); | |
387 | + if (flags & MS_BIND) | |
388 | + audit_log_format(ab, flags & MS_REC ? ", rbind" : ", bind"); | |
389 | + if (flags & MS_MOVE) | |
390 | + audit_log_format(ab, ", move"); | |
391 | + if (flags & MS_SILENT) | |
392 | + audit_log_format(ab, ", silent"); | |
393 | + if (flags & MS_POSIXACL) | |
394 | + audit_log_format(ab, ", acl"); | |
395 | + if (flags & MS_UNBINDABLE) | |
396 | + audit_log_format(ab, flags & MS_REC ? ", runbindable" : | |
397 | + ", unbindable"); | |
398 | + if (flags & MS_PRIVATE) | |
399 | + audit_log_format(ab, flags & MS_REC ? ", rprivate" : | |
400 | + ", private"); | |
401 | + if (flags & MS_SLAVE) | |
402 | + audit_log_format(ab, flags & MS_REC ? ", rslave" : | |
403 | + ", slave"); | |
404 | + if (flags & MS_SHARED) | |
405 | + audit_log_format(ab, flags & MS_REC ? ", rshared" : | |
406 | + ", shared"); | |
407 | + if (flags & MS_RELATIME) | |
408 | + audit_log_format(ab, ", relatime"); | |
409 | + if (flags & MS_I_VERSION) | |
410 | + audit_log_format(ab, ", iversion"); | |
411 | + if (flags & MS_STRICTATIME) | |
412 | + audit_log_format(ab, ", strictatime"); | |
413 | + if (flags & MS_NOUSER) | |
414 | + audit_log_format(ab, ", nouser"); | |
415 | +} | |
416 | + | |
417 | +/** | |
418 | + * audit_cb - call back for mount specific audit fields | |
419 | + * @ab: audit_buffer (NOT NULL) | |
420 | + * @va: audit struct to audit values of (NOT NULL) | |
421 | + */ | |
422 | +static void audit_cb(struct audit_buffer *ab, void *va) | |
423 | +{ | |
424 | + struct common_audit_data *sa = va; | |
425 | + | |
426 | + if (sa->aad->mnt.type) { | |
427 | + audit_log_format(ab, " fstype="); | |
428 | + audit_log_untrustedstring(ab, sa->aad->mnt.type); | |
429 | + } | |
430 | + if (sa->aad->mnt.src_name) { | |
431 | + audit_log_format(ab, " srcname="); | |
432 | + audit_log_untrustedstring(ab, sa->aad->mnt.src_name); | |
433 | + } | |
434 | + if (sa->aad->mnt.trans) { | |
435 | + audit_log_format(ab, " trans="); | |
436 | + audit_log_untrustedstring(ab, sa->aad->mnt.trans); | |
437 | + } | |
438 | + if (sa->aad->mnt.flags || sa->aad->op == OP_MOUNT) { | |
439 | + audit_log_format(ab, " flags=\""); | |
440 | + audit_mnt_flags(ab, sa->aad->mnt.flags); | |
441 | + audit_log_format(ab, "\""); | |
442 | + } | |
443 | + if (sa->aad->mnt.data) { | |
444 | + audit_log_format(ab, " options="); | |
445 | + audit_log_untrustedstring(ab, sa->aad->mnt.data); | |
446 | + } | |
447 | +} | |
448 | + | |
449 | +/** | |
450 | + * audit_mount - handle the auditing of mount operations | |
451 | + * @profile: the profile being enforced (NOT NULL) | |
452 | + * @gfp: allocation flags | |
453 | + * @op: operation being mediated (NOT NULL) | |
454 | + * @name: name of object being mediated (MAYBE NULL) | |
455 | + * @src_name: src_name of object being mediated (MAYBE_NULL) | |
456 | + * @type: type of filesystem (MAYBE_NULL) | |
457 | + * @trans: name of trans (MAYBE NULL) | |
458 | + * @flags: filesystem idependent mount flags | |
459 | + * @data: filesystem mount flags | |
460 | + * @request: permissions requested | |
461 | + * @perms: the permissions computed for the request (NOT NULL) | |
462 | + * @info: extra information message (MAYBE NULL) | |
463 | + * @error: 0 if operation allowed else failure error code | |
464 | + * | |
465 | + * Returns: %0 or error on failure | |
466 | + */ | |
467 | +static int audit_mount(struct aa_profile *profile, gfp_t gfp, int op, | |
468 | + const char *name, const char *src_name, | |
469 | + const char *type, const char *trans, | |
470 | + unsigned long flags, const void *data, u32 request, | |
471 | + struct file_perms *perms, const char *info, int error) | |
472 | +{ | |
473 | + int audit_type = AUDIT_APPARMOR_AUTO; | |
474 | + struct common_audit_data sa = { }; | |
475 | + struct apparmor_audit_data aad = { }; | |
476 | + | |
477 | + if (likely(!error)) { | |
478 | + u32 mask = perms->audit; | |
479 | + | |
480 | + if (unlikely(AUDIT_MODE(profile) == AUDIT_ALL)) | |
481 | + mask = 0xffff; | |
482 | + | |
483 | + /* mask off perms that are not being force audited */ | |
484 | + request &= mask; | |
485 | + | |
486 | + if (likely(!request)) | |
487 | + return 0; | |
488 | + audit_type = AUDIT_APPARMOR_AUDIT; | |
489 | + } else { | |
490 | + /* only report permissions that were denied */ | |
491 | + request = request & ~perms->allow; | |
492 | + | |
493 | + if (request & perms->kill) | |
494 | + audit_type = AUDIT_APPARMOR_KILL; | |
495 | + | |
496 | + /* quiet known rejects, assumes quiet and kill do not overlap */ | |
497 | + if ((request & perms->quiet) && | |
498 | + AUDIT_MODE(profile) != AUDIT_NOQUIET && | |
499 | + AUDIT_MODE(profile) != AUDIT_ALL) | |
500 | + request &= ~perms->quiet; | |
501 | + | |
502 | + if (!request) | |
503 | + return COMPLAIN_MODE(profile) ? | |
504 | + complain_error(error) : error; | |
505 | + } | |
506 | + | |
507 | + sa.type = LSM_AUDIT_DATA_NONE; | |
508 | + sa.aad = &aad; | |
509 | + sa.aad->op = op; | |
510 | + sa.aad->name = name; | |
511 | + sa.aad->mnt.src_name = src_name; | |
512 | + sa.aad->mnt.type = type; | |
513 | + sa.aad->mnt.trans = trans; | |
514 | + sa.aad->mnt.flags = flags; | |
515 | + if (data && (perms->audit & AA_AUDIT_DATA)) | |
516 | + sa.aad->mnt.data = data; | |
517 | + sa.aad->info = info; | |
518 | + sa.aad->error = error; | |
519 | + | |
520 | + return aa_audit(audit_type, profile, gfp, &sa, audit_cb); | |
521 | +} | |
522 | + | |
523 | +/** | |
524 | + * match_mnt_flags - Do an ordered match on mount flags | |
525 | + * @dfa: dfa to match against | |
526 | + * @state: state to start in | |
527 | + * @flags: mount flags to match against | |
528 | + * | |
529 | + * Mount flags are encoded as an ordered match. This is done instead of | |
530 | + * checking against a simple bitmask, to allow for logical operations | |
531 | + * on the flags. | |
532 | + * | |
533 | + * Returns: next state after flags match | |
534 | + */ | |
535 | +static unsigned int match_mnt_flags(struct aa_dfa *dfa, unsigned int state, | |
536 | + unsigned long flags) | |
537 | +{ | |
538 | + unsigned int i; | |
539 | + | |
540 | + for (i = 0; i <= 31 ; ++i) { | |
541 | + if ((1 << i) & flags) | |
542 | + state = aa_dfa_next(dfa, state, i + 1); | |
543 | + } | |
544 | + | |
545 | + return state; | |
546 | +} | |
547 | + | |
548 | +/** | |
549 | + * compute_mnt_perms - compute mount permission associated with @state | |
550 | + * @dfa: dfa to match against (NOT NULL) | |
551 | + * @state: state match finished in | |
552 | + * | |
553 | + * Returns: mount permissions | |
554 | + */ | |
555 | +static struct file_perms compute_mnt_perms(struct aa_dfa *dfa, | |
556 | + unsigned int state) | |
557 | +{ | |
558 | + struct file_perms perms; | |
559 | + | |
560 | + perms.kill = 0; | |
561 | + perms.allow = dfa_user_allow(dfa, state); | |
562 | + perms.audit = dfa_user_audit(dfa, state); | |
563 | + perms.quiet = dfa_user_quiet(dfa, state); | |
564 | + perms.xindex = dfa_user_xindex(dfa, state); | |
565 | + | |
566 | + return perms; | |
567 | +} | |
568 | + | |
569 | +static const char const *mnt_info_table[] = { | |
570 | + "match succeeded", | |
571 | + "failed mntpnt match", | |
572 | + "failed srcname match", | |
573 | + "failed type match", | |
574 | + "failed flags match", | |
575 | + "failed data match" | |
576 | +}; | |
577 | + | |
578 | +/* | |
579 | + * Returns 0 on success else element that match failed in, this is the | |
580 | + * index into the mnt_info_table above | |
581 | + */ | |
582 | +static int do_match_mnt(struct aa_dfa *dfa, unsigned int start, | |
583 | + const char *mntpnt, const char *devname, | |
584 | + const char *type, unsigned long flags, | |
585 | + void *data, bool binary, struct file_perms *perms) | |
586 | +{ | |
587 | + unsigned int state; | |
588 | + | |
589 | + state = aa_dfa_match(dfa, start, mntpnt); | |
590 | + state = aa_dfa_null_transition(dfa, state); | |
591 | + if (!state) | |
592 | + return 1; | |
593 | + | |
594 | + if (devname) | |
595 | + state = aa_dfa_match(dfa, state, devname); | |
596 | + state = aa_dfa_null_transition(dfa, state); | |
597 | + if (!state) | |
598 | + return 2; | |
599 | + | |
600 | + if (type) | |
601 | + state = aa_dfa_match(dfa, state, type); | |
602 | + state = aa_dfa_null_transition(dfa, state); | |
603 | + if (!state) | |
604 | + return 3; | |
605 | + | |
606 | + state = match_mnt_flags(dfa, state, flags); | |
607 | + if (!state) | |
608 | + return 4; | |
609 | + *perms = compute_mnt_perms(dfa, state); | |
610 | + if (perms->allow & AA_MAY_MOUNT) | |
611 | + return 0; | |
612 | + | |
613 | + /* only match data if not binary and the DFA flags data is expected */ | |
614 | + if (data && !binary && (perms->allow & AA_CONT_MATCH)) { | |
615 | + state = aa_dfa_null_transition(dfa, state); | |
616 | + if (!state) | |
617 | + return 4; | |
618 | + | |
619 | + state = aa_dfa_match(dfa, state, data); | |
620 | + if (!state) | |
621 | + return 5; | |
622 | + *perms = compute_mnt_perms(dfa, state); | |
623 | + if (perms->allow & AA_MAY_MOUNT) | |
624 | + return 0; | |
625 | + } | |
626 | + | |
627 | + /* failed at end of flags match */ | |
628 | + return 4; | |
629 | +} | |
630 | + | |
631 | +/** | |
632 | + * match_mnt - handle path matching for mount | |
633 | + * @profile: the confining profile | |
634 | + * @mntpnt: string for the mntpnt (NOT NULL) | |
635 | + * @devname: string for the devname/src_name (MAYBE NULL) | |
636 | + * @type: string for the dev type (MAYBE NULL) | |
637 | + * @flags: mount flags to match | |
638 | + * @data: fs mount data (MAYBE NULL) | |
639 | + * @binary: whether @data is binary | |
640 | + * @perms: Returns: permission found by the match | |
641 | + * @info: Returns: infomation string about the match for logging | |
642 | + * | |
643 | + * Returns: 0 on success else error | |
644 | + */ | |
645 | +static int match_mnt(struct aa_profile *profile, const char *mntpnt, | |
646 | + const char *devname, const char *type, | |
647 | + unsigned long flags, void *data, bool binary, | |
648 | + struct file_perms *perms, const char **info) | |
649 | +{ | |
650 | + int pos; | |
651 | + | |
652 | + if (!profile->policy.dfa) | |
653 | + return -EACCES; | |
654 | + | |
655 | + pos = do_match_mnt(profile->policy.dfa, | |
656 | + profile->policy.start[AA_CLASS_MOUNT], | |
657 | + mntpnt, devname, type, flags, data, binary, perms); | |
658 | + if (pos) { | |
659 | + *info = mnt_info_table[pos]; | |
660 | + return -EACCES; | |
661 | + } | |
662 | + | |
663 | + return 0; | |
664 | +} | |
665 | + | |
666 | +static int path_flags(struct aa_profile *profile, struct path *path) | |
667 | +{ | |
668 | + return profile->path_flags | | |
669 | + S_ISDIR(path->dentry->d_inode->i_mode) ? PATH_IS_DIR : 0; | |
670 | +} | |
671 | + | |
672 | +int aa_remount(struct aa_profile *profile, struct path *path, | |
673 | + unsigned long flags, void *data) | |
674 | +{ | |
675 | + struct file_perms perms = { }; | |
676 | + const char *name, *info = NULL; | |
677 | + char *buffer = NULL; | |
678 | + int binary, error; | |
679 | + | |
680 | + binary = path->dentry->d_sb->s_type->fs_flags & FS_BINARY_MOUNTDATA; | |
681 | + | |
682 | + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, | |
683 | + &info); | |
684 | + if (error) | |
685 | + goto audit; | |
686 | + | |
687 | + error = match_mnt(profile, name, NULL, NULL, flags, data, binary, | |
688 | + &perms, &info); | |
689 | + | |
690 | +audit: | |
691 | + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, NULL, NULL, | |
692 | + NULL, flags, data, AA_MAY_MOUNT, &perms, info, | |
693 | + error); | |
694 | + kfree(buffer); | |
695 | + | |
696 | + return error; | |
697 | +} | |
698 | + | |
699 | +int aa_bind_mount(struct aa_profile *profile, struct path *path, | |
700 | + const char *dev_name, unsigned long flags) | |
701 | +{ | |
702 | + struct file_perms perms = { }; | |
703 | + char *buffer = NULL, *old_buffer = NULL; | |
704 | + const char *name, *old_name = NULL, *info = NULL; | |
705 | + struct path old_path; | |
706 | + int error; | |
707 | + | |
708 | + if (!dev_name || !*dev_name) | |
709 | + return -EINVAL; | |
710 | + | |
711 | + flags &= MS_REC | MS_BIND; | |
712 | + | |
713 | + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, | |
714 | + &info); | |
715 | + if (error) | |
716 | + goto audit; | |
717 | + | |
718 | + error = kern_path(dev_name, LOOKUP_FOLLOW|LOOKUP_AUTOMOUNT, &old_path); | |
719 | + if (error) | |
720 | + goto audit; | |
721 | + | |
722 | + error = aa_path_name(&old_path, path_flags(profile, &old_path), | |
723 | + &old_buffer, &old_name, &info); | |
724 | + path_put(&old_path); | |
725 | + if (error) | |
726 | + goto audit; | |
727 | + | |
728 | + error = match_mnt(profile, name, old_name, NULL, flags, NULL, 0, | |
729 | + &perms, &info); | |
730 | + | |
731 | +audit: | |
732 | + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, old_name, | |
733 | + NULL, NULL, flags, NULL, AA_MAY_MOUNT, &perms, | |
734 | + info, error); | |
735 | + kfree(buffer); | |
736 | + kfree(old_buffer); | |
737 | + | |
738 | + return error; | |
739 | +} | |
740 | + | |
741 | +int aa_mount_change_type(struct aa_profile *profile, struct path *path, | |
742 | + unsigned long flags) | |
743 | +{ | |
744 | + struct file_perms perms = { }; | |
745 | + char *buffer = NULL; | |
746 | + const char *name, *info = NULL; | |
747 | + int error; | |
748 | + | |
749 | + /* These are the flags allowed by do_change_type() */ | |
750 | + flags &= (MS_REC | MS_SILENT | MS_SHARED | MS_PRIVATE | MS_SLAVE | | |
751 | + MS_UNBINDABLE); | |
752 | + | |
753 | + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, | |
754 | + &info); | |
755 | + if (error) | |
756 | + goto audit; | |
757 | + | |
758 | + error = match_mnt(profile, name, NULL, NULL, flags, NULL, 0, &perms, | |
759 | + &info); | |
760 | + | |
761 | +audit: | |
762 | + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, NULL, NULL, | |
763 | + NULL, flags, NULL, AA_MAY_MOUNT, &perms, info, | |
764 | + error); | |
765 | + kfree(buffer); | |
766 | + | |
767 | + return error; | |
768 | +} | |
769 | + | |
770 | +int aa_move_mount(struct aa_profile *profile, struct path *path, | |
771 | + const char *orig_name) | |
772 | +{ | |
773 | + struct file_perms perms = { }; | |
774 | + char *buffer = NULL, *old_buffer = NULL; | |
775 | + const char *name, *old_name = NULL, *info = NULL; | |
776 | + struct path old_path; | |
777 | + int error; | |
778 | + | |
779 | + if (!orig_name || !*orig_name) | |
780 | + return -EINVAL; | |
781 | + | |
782 | + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, | |
783 | + &info); | |
784 | + if (error) | |
785 | + goto audit; | |
786 | + | |
787 | + error = kern_path(orig_name, LOOKUP_FOLLOW, &old_path); | |
788 | + if (error) | |
789 | + goto audit; | |
790 | + | |
791 | + error = aa_path_name(&old_path, path_flags(profile, &old_path), | |
792 | + &old_buffer, &old_name, &info); | |
793 | + path_put(&old_path); | |
794 | + if (error) | |
795 | + goto audit; | |
796 | + | |
797 | + error = match_mnt(profile, name, old_name, NULL, MS_MOVE, NULL, 0, | |
798 | + &perms, &info); | |
799 | + | |
800 | +audit: | |
801 | + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, old_name, | |
802 | + NULL, NULL, MS_MOVE, NULL, AA_MAY_MOUNT, &perms, | |
803 | + info, error); | |
804 | + kfree(buffer); | |
805 | + kfree(old_buffer); | |
806 | + | |
807 | + return error; | |
808 | +} | |
809 | + | |
810 | +int aa_new_mount(struct aa_profile *profile, const char *orig_dev_name, | |
811 | + struct path *path, const char *type, unsigned long flags, | |
812 | + void *data) | |
813 | +{ | |
814 | + struct file_perms perms = { }; | |
815 | + char *buffer = NULL, *dev_buffer = NULL; | |
816 | + const char *name = NULL, *dev_name = NULL, *info = NULL; | |
817 | + int binary = 1; | |
818 | + int error; | |
819 | + | |
820 | + dev_name = orig_dev_name; | |
821 | + if (type) { | |
822 | + int requires_dev; | |
823 | + struct file_system_type *fstype = get_fs_type(type); | |
824 | + if (!fstype) | |
825 | + return -ENODEV; | |
826 | + | |
827 | + binary = fstype->fs_flags & FS_BINARY_MOUNTDATA; | |
828 | + requires_dev = fstype->fs_flags & FS_REQUIRES_DEV; | |
829 | + put_filesystem(fstype); | |
830 | + | |
831 | + if (requires_dev) { | |
832 | + struct path dev_path; | |
833 | + | |
834 | + if (!dev_name || !*dev_name) { | |
835 | + error = -ENOENT; | |
836 | + goto out; | |
837 | + } | |
838 | + | |
839 | + error = kern_path(dev_name, LOOKUP_FOLLOW, &dev_path); | |
840 | + if (error) | |
841 | + goto audit; | |
842 | + | |
843 | + error = aa_path_name(&dev_path, | |
844 | + path_flags(profile, &dev_path), | |
845 | + &dev_buffer, &dev_name, &info); | |
846 | + path_put(&dev_path); | |
847 | + if (error) | |
848 | + goto audit; | |
849 | + } | |
850 | + } | |
851 | + | |
852 | + error = aa_path_name(path, path_flags(profile, path), &buffer, &name, | |
853 | + &info); | |
854 | + if (error) | |
855 | + goto audit; | |
856 | + | |
857 | + error = match_mnt(profile, name, dev_name, type, flags, data, binary, | |
858 | + &perms, &info); | |
859 | + | |
860 | +audit: | |
861 | + error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, dev_name, | |
862 | + type, NULL, flags, data, AA_MAY_MOUNT, &perms, info, | |
863 | + error); | |
864 | + kfree(buffer); | |
865 | + kfree(dev_buffer); | |
866 | + | |
867 | +out: | |
868 | + return error; | |
869 | + | |
870 | +} | |
871 | + | |
872 | +int aa_umount(struct aa_profile *profile, struct vfsmount *mnt, int flags) | |
873 | +{ | |
874 | + struct file_perms perms = { }; | |
875 | + char *buffer = NULL; | |
876 | + const char *name, *info = NULL; | |
877 | + int error; | |
878 | + | |
879 | + struct path path = { mnt, mnt->mnt_root }; | |
880 | + error = aa_path_name(&path, path_flags(profile, &path), &buffer, &name, | |
881 | + &info); | |
882 | + if (error) | |
883 | + goto audit; | |
884 | + | |
885 | + if (!error && profile->policy.dfa) { | |
886 | + unsigned int state; | |
887 | + state = aa_dfa_match(profile->policy.dfa, | |
888 | + profile->policy.start[AA_CLASS_MOUNT], | |
889 | + name); | |
890 | + perms = compute_mnt_perms(profile->policy.dfa, state); | |
891 | + } | |
892 | + | |
893 | + if (AA_MAY_UMOUNT & ~perms.allow) | |
894 | + error = -EACCES; | |
895 | + | |
896 | +audit: | |
897 | + error = audit_mount(profile, GFP_KERNEL, OP_UMOUNT, name, NULL, NULL, | |
898 | + NULL, 0, NULL, AA_MAY_UMOUNT, &perms, info, error); | |
899 | + kfree(buffer); | |
900 | + | |
901 | + return error; | |
902 | +} | |
903 | + | |
904 | +int aa_pivotroot(struct aa_profile *profile, struct path *old_path, | |
905 | + struct path *new_path) | |
906 | +{ | |
907 | + struct file_perms perms = { }; | |
908 | + struct aa_profile *target = NULL; | |
909 | + char *old_buffer = NULL, *new_buffer = NULL; | |
910 | + const char *old_name, *new_name = NULL, *info = NULL; | |
911 | + int error; | |
912 | + | |
913 | + error = aa_path_name(old_path, path_flags(profile, old_path), | |
914 | + &old_buffer, &old_name, &info); | |
915 | + if (error) | |
916 | + goto audit; | |
917 | + | |
918 | + error = aa_path_name(new_path, path_flags(profile, new_path), | |
919 | + &new_buffer, &new_name, &info); | |
920 | + if (error) | |
921 | + goto audit; | |
922 | + | |
923 | + if (profile->policy.dfa) { | |
924 | + unsigned int state; | |
925 | + state = aa_dfa_match(profile->policy.dfa, | |
926 | + profile->policy.start[AA_CLASS_MOUNT], | |
927 | + new_name); | |
928 | + state = aa_dfa_null_transition(profile->policy.dfa, state); | |
929 | + state = aa_dfa_match(profile->policy.dfa, state, old_name); | |
930 | + perms = compute_mnt_perms(profile->policy.dfa, state); | |
931 | + } | |
932 | + | |
933 | + if (AA_MAY_PIVOTROOT & perms.allow) { | |
934 | + if ((perms.xindex & AA_X_TYPE_MASK) == AA_X_TABLE) { | |
935 | + target = x_table_lookup(profile, perms.xindex); | |
936 | + if (!target) | |
937 | + error = -ENOENT; | |
938 | + else | |
939 | + error = aa_replace_current_profile(target); | |
940 | + } | |
941 | + } else | |
942 | + error = -EACCES; | |
943 | + | |
944 | +audit: | |
945 | + error = audit_mount(profile, GFP_KERNEL, OP_PIVOTROOT, new_name, | |
946 | + old_name, NULL, target ? target->base.name : NULL, | |
947 | + 0, NULL, AA_MAY_PIVOTROOT, &perms, info, error); | |
948 | + aa_put_profile(target); | |
949 | + kfree(old_buffer); | |
950 | + kfree(new_buffer); | |
951 | + | |
952 | + return error; | |
953 | +} | |
954 | -- | |
955 | 1.8.3.2 | |
956 |