diff options
author | John Johansen <john.johansen@canonical.com> | 2013-07-11 00:13:43 -0400 |
---|---|---|
committer | John Johansen <john.johansen@canonical.com> | 2013-08-14 14:42:07 -0400 |
commit | 0d259f043f5f60f74c4fd020aac190cb6450e918 (patch) | |
tree | 92fed6a02a1dc6069d7d92e14f2418c85f936303 /security/apparmor/apparmorfs.c | |
parent | 038165070aa55375d4bdd2f84b34a486feca63d6 (diff) |
apparmor: add interface files for profiles and namespaces
Add basic interface files to access namespace and profile information.
The interface files are created when a profile is loaded and removed
when the profile or namespace is removed.
Signed-off-by: John Johansen <john.johansen@canonical.com>
Diffstat (limited to 'security/apparmor/apparmorfs.c')
-rw-r--r-- | security/apparmor/apparmorfs.c | 322 |
1 files changed, 312 insertions, 10 deletions
diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 3ed56e21a9fd..0fdd08c6ea59 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c | |||
@@ -12,6 +12,7 @@ | |||
12 | * License. | 12 | * License. |
13 | */ | 13 | */ |
14 | 14 | ||
15 | #include <linux/ctype.h> | ||
15 | #include <linux/security.h> | 16 | #include <linux/security.h> |
16 | #include <linux/vmalloc.h> | 17 | #include <linux/vmalloc.h> |
17 | #include <linux/module.h> | 18 | #include <linux/module.h> |
@@ -28,6 +29,45 @@ | |||
28 | #include "include/resource.h" | 29 | #include "include/resource.h" |
29 | 30 | ||
30 | /** | 31 | /** |
32 | * aa_mangle_name - mangle a profile name to std profile layout form | ||
33 | * @name: profile name to mangle (NOT NULL) | ||
34 | * @target: buffer to store mangled name, same length as @name (MAYBE NULL) | ||
35 | * | ||
36 | * Returns: length of mangled name | ||
37 | */ | ||
38 | static int mangle_name(char *name, char *target) | ||
39 | { | ||
40 | char *t = target; | ||
41 | |||
42 | while (*name == '/' || *name == '.') | ||
43 | name++; | ||
44 | |||
45 | if (target) { | ||
46 | for (; *name; name++) { | ||
47 | if (*name == '/') | ||
48 | *(t)++ = '.'; | ||
49 | else if (isspace(*name)) | ||
50 | *(t)++ = '_'; | ||
51 | else if (isalnum(*name) || strchr("._-", *name)) | ||
52 | *(t)++ = *name; | ||
53 | } | ||
54 | |||
55 | *t = 0; | ||
56 | } else { | ||
57 | int len = 0; | ||
58 | for (; *name; name++) { | ||
59 | if (isalnum(*name) || isspace(*name) || | ||
60 | strchr("/._-", *name)) | ||
61 | len++; | ||
62 | } | ||
63 | |||
64 | return len; | ||
65 | } | ||
66 | |||
67 | return t - target; | ||
68 | } | ||
69 | |||
70 | /** | ||
31 | * aa_simple_write_to_buffer - common routine for getting policy from user | 71 | * aa_simple_write_to_buffer - common routine for getting policy from user |
32 | * @op: operation doing the user buffer copy | 72 | * @op: operation doing the user buffer copy |
33 | * @userbuf: user buffer to copy data from (NOT NULL) | 73 | * @userbuf: user buffer to copy data from (NOT NULL) |
@@ -182,8 +222,263 @@ const struct file_operations aa_fs_seq_file_ops = { | |||
182 | .release = single_release, | 222 | .release = single_release, |
183 | }; | 223 | }; |
184 | 224 | ||
185 | /** Base file system setup **/ | 225 | static int aa_fs_seq_profile_open(struct inode *inode, struct file *file, |
226 | int (*show)(struct seq_file *, void *)) | ||
227 | { | ||
228 | struct aa_replacedby *r = aa_get_replacedby(inode->i_private); | ||
229 | int error = single_open(file, show, r); | ||
230 | |||
231 | if (error) { | ||
232 | file->private_data = NULL; | ||
233 | aa_put_replacedby(r); | ||
234 | } | ||
235 | |||
236 | return error; | ||
237 | } | ||
238 | |||
239 | static int aa_fs_seq_profile_release(struct inode *inode, struct file *file) | ||
240 | { | ||
241 | struct seq_file *seq = (struct seq_file *) file->private_data; | ||
242 | if (seq) | ||
243 | aa_put_replacedby(seq->private); | ||
244 | return single_release(inode, file); | ||
245 | } | ||
246 | |||
247 | static int aa_fs_seq_profname_show(struct seq_file *seq, void *v) | ||
248 | { | ||
249 | struct aa_replacedby *r = seq->private; | ||
250 | struct aa_profile *profile = aa_get_profile_rcu(&r->profile); | ||
251 | seq_printf(seq, "%s\n", profile->base.name); | ||
252 | aa_put_profile(profile); | ||
253 | |||
254 | return 0; | ||
255 | } | ||
256 | |||
257 | static int aa_fs_seq_profname_open(struct inode *inode, struct file *file) | ||
258 | { | ||
259 | return aa_fs_seq_profile_open(inode, file, aa_fs_seq_profname_show); | ||
260 | } | ||
261 | |||
262 | static const struct file_operations aa_fs_profname_fops = { | ||
263 | .owner = THIS_MODULE, | ||
264 | .open = aa_fs_seq_profname_open, | ||
265 | .read = seq_read, | ||
266 | .llseek = seq_lseek, | ||
267 | .release = aa_fs_seq_profile_release, | ||
268 | }; | ||
269 | |||
270 | static int aa_fs_seq_profmode_show(struct seq_file *seq, void *v) | ||
271 | { | ||
272 | struct aa_replacedby *r = seq->private; | ||
273 | struct aa_profile *profile = aa_get_profile_rcu(&r->profile); | ||
274 | seq_printf(seq, "%s\n", aa_profile_mode_names[profile->mode]); | ||
275 | aa_put_profile(profile); | ||
276 | |||
277 | return 0; | ||
278 | } | ||
279 | |||
280 | static int aa_fs_seq_profmode_open(struct inode *inode, struct file *file) | ||
281 | { | ||
282 | return aa_fs_seq_profile_open(inode, file, aa_fs_seq_profmode_show); | ||
283 | } | ||
284 | |||
285 | static const struct file_operations aa_fs_profmode_fops = { | ||
286 | .owner = THIS_MODULE, | ||
287 | .open = aa_fs_seq_profmode_open, | ||
288 | .read = seq_read, | ||
289 | .llseek = seq_lseek, | ||
290 | .release = aa_fs_seq_profile_release, | ||
291 | }; | ||
292 | |||
293 | /** fns to setup dynamic per profile/namespace files **/ | ||
294 | void __aa_fs_profile_rmdir(struct aa_profile *profile) | ||
295 | { | ||
296 | struct aa_profile *child; | ||
297 | int i; | ||
298 | |||
299 | if (!profile) | ||
300 | return; | ||
301 | |||
302 | list_for_each_entry(child, &profile->base.profiles, base.list) | ||
303 | __aa_fs_profile_rmdir(child); | ||
304 | |||
305 | for (i = AAFS_PROF_SIZEOF - 1; i >= 0; --i) { | ||
306 | struct aa_replacedby *r; | ||
307 | if (!profile->dents[i]) | ||
308 | continue; | ||
309 | |||
310 | r = profile->dents[i]->d_inode->i_private; | ||
311 | securityfs_remove(profile->dents[i]); | ||
312 | aa_put_replacedby(r); | ||
313 | profile->dents[i] = NULL; | ||
314 | } | ||
315 | } | ||
316 | |||
317 | void __aa_fs_profile_migrate_dents(struct aa_profile *old, | ||
318 | struct aa_profile *new) | ||
319 | { | ||
320 | int i; | ||
321 | |||
322 | for (i = 0; i < AAFS_PROF_SIZEOF; i++) { | ||
323 | new->dents[i] = old->dents[i]; | ||
324 | old->dents[i] = NULL; | ||
325 | } | ||
326 | } | ||
327 | |||
328 | static struct dentry *create_profile_file(struct dentry *dir, const char *name, | ||
329 | struct aa_profile *profile, | ||
330 | const struct file_operations *fops) | ||
331 | { | ||
332 | struct aa_replacedby *r = aa_get_replacedby(profile->replacedby); | ||
333 | struct dentry *dent; | ||
334 | |||
335 | dent = securityfs_create_file(name, S_IFREG | 0444, dir, r, fops); | ||
336 | if (IS_ERR(dent)) | ||
337 | aa_put_replacedby(r); | ||
338 | |||
339 | return dent; | ||
340 | } | ||
341 | |||
342 | /* requires lock be held */ | ||
343 | int __aa_fs_profile_mkdir(struct aa_profile *profile, struct dentry *parent) | ||
344 | { | ||
345 | struct aa_profile *child; | ||
346 | struct dentry *dent = NULL, *dir; | ||
347 | int error; | ||
348 | |||
349 | if (!parent) { | ||
350 | struct aa_profile *p; | ||
351 | p = aa_deref_parent(profile); | ||
352 | dent = prof_dir(p); | ||
353 | /* adding to parent that previously didn't have children */ | ||
354 | dent = securityfs_create_dir("profiles", dent); | ||
355 | if (IS_ERR(dent)) | ||
356 | goto fail; | ||
357 | prof_child_dir(p) = parent = dent; | ||
358 | } | ||
359 | |||
360 | if (!profile->dirname) { | ||
361 | int len, id_len; | ||
362 | len = mangle_name(profile->base.name, NULL); | ||
363 | id_len = snprintf(NULL, 0, ".%ld", profile->ns->uniq_id); | ||
364 | |||
365 | profile->dirname = kmalloc(len + id_len + 1, GFP_KERNEL); | ||
366 | if (!profile->dirname) | ||
367 | goto fail; | ||
368 | |||
369 | mangle_name(profile->base.name, profile->dirname); | ||
370 | sprintf(profile->dirname + len, ".%ld", profile->ns->uniq_id++); | ||
371 | } | ||
372 | |||
373 | dent = securityfs_create_dir(profile->dirname, parent); | ||
374 | if (IS_ERR(dent)) | ||
375 | goto fail; | ||
376 | prof_dir(profile) = dir = dent; | ||
377 | |||
378 | dent = create_profile_file(dir, "name", profile, &aa_fs_profname_fops); | ||
379 | if (IS_ERR(dent)) | ||
380 | goto fail; | ||
381 | profile->dents[AAFS_PROF_NAME] = dent; | ||
382 | |||
383 | dent = create_profile_file(dir, "mode", profile, &aa_fs_profmode_fops); | ||
384 | if (IS_ERR(dent)) | ||
385 | goto fail; | ||
386 | profile->dents[AAFS_PROF_MODE] = dent; | ||
387 | |||
388 | list_for_each_entry(child, &profile->base.profiles, base.list) { | ||
389 | error = __aa_fs_profile_mkdir(child, prof_child_dir(profile)); | ||
390 | if (error) | ||
391 | goto fail2; | ||
392 | } | ||
393 | |||
394 | return 0; | ||
395 | |||
396 | fail: | ||
397 | error = PTR_ERR(dent); | ||
398 | |||
399 | fail2: | ||
400 | __aa_fs_profile_rmdir(profile); | ||
401 | |||
402 | return error; | ||
403 | } | ||
404 | |||
405 | void __aa_fs_namespace_rmdir(struct aa_namespace *ns) | ||
406 | { | ||
407 | struct aa_namespace *sub; | ||
408 | struct aa_profile *child; | ||
409 | int i; | ||
410 | |||
411 | if (!ns) | ||
412 | return; | ||
413 | |||
414 | list_for_each_entry(child, &ns->base.profiles, base.list) | ||
415 | __aa_fs_profile_rmdir(child); | ||
416 | |||
417 | list_for_each_entry(sub, &ns->sub_ns, base.list) { | ||
418 | mutex_lock(&sub->lock); | ||
419 | __aa_fs_namespace_rmdir(sub); | ||
420 | mutex_unlock(&sub->lock); | ||
421 | } | ||
422 | |||
423 | for (i = AAFS_NS_SIZEOF - 1; i >= 0; --i) { | ||
424 | securityfs_remove(ns->dents[i]); | ||
425 | ns->dents[i] = NULL; | ||
426 | } | ||
427 | } | ||
428 | |||
429 | int __aa_fs_namespace_mkdir(struct aa_namespace *ns, struct dentry *parent, | ||
430 | const char *name) | ||
431 | { | ||
432 | struct aa_namespace *sub; | ||
433 | struct aa_profile *child; | ||
434 | struct dentry *dent, *dir; | ||
435 | int error; | ||
436 | |||
437 | if (!name) | ||
438 | name = ns->base.name; | ||
439 | |||
440 | dent = securityfs_create_dir(name, parent); | ||
441 | if (IS_ERR(dent)) | ||
442 | goto fail; | ||
443 | ns_dir(ns) = dir = dent; | ||
444 | |||
445 | dent = securityfs_create_dir("profiles", dir); | ||
446 | if (IS_ERR(dent)) | ||
447 | goto fail; | ||
448 | ns_subprofs_dir(ns) = dent; | ||
186 | 449 | ||
450 | dent = securityfs_create_dir("namespaces", dir); | ||
451 | if (IS_ERR(dent)) | ||
452 | goto fail; | ||
453 | ns_subns_dir(ns) = dent; | ||
454 | |||
455 | list_for_each_entry(child, &ns->base.profiles, base.list) { | ||
456 | error = __aa_fs_profile_mkdir(child, ns_subprofs_dir(ns)); | ||
457 | if (error) | ||
458 | goto fail2; | ||
459 | } | ||
460 | |||
461 | list_for_each_entry(sub, &ns->sub_ns, base.list) { | ||
462 | mutex_lock(&sub->lock); | ||
463 | error = __aa_fs_namespace_mkdir(sub, ns_subns_dir(ns), NULL); | ||
464 | mutex_unlock(&sub->lock); | ||
465 | if (error) | ||
466 | goto fail2; | ||
467 | } | ||
468 | |||
469 | return 0; | ||
470 | |||
471 | fail: | ||
472 | error = PTR_ERR(dent); | ||
473 | |||
474 | fail2: | ||
475 | __aa_fs_namespace_rmdir(ns); | ||
476 | |||
477 | return error; | ||
478 | } | ||
479 | |||
480 | |||
481 | /** Base file system setup **/ | ||
187 | static struct aa_fs_entry aa_fs_entry_file[] = { | 482 | static struct aa_fs_entry aa_fs_entry_file[] = { |
188 | AA_FS_FILE_STRING("mask", "create read write exec append mmap_exec " \ | 483 | AA_FS_FILE_STRING("mask", "create read write exec append mmap_exec " \ |
189 | "link lock"), | 484 | "link lock"), |
@@ -246,6 +541,7 @@ static int __init aafs_create_file(struct aa_fs_entry *fs_file, | |||
246 | return error; | 541 | return error; |
247 | } | 542 | } |
248 | 543 | ||
544 | static void __init aafs_remove_dir(struct aa_fs_entry *fs_dir); | ||
249 | /** | 545 | /** |
250 | * aafs_create_dir - recursively create a directory entry in the securityfs | 546 | * aafs_create_dir - recursively create a directory entry in the securityfs |
251 | * @fs_dir: aa_fs_entry (and all child entries) to build (NOT NULL) | 547 | * @fs_dir: aa_fs_entry (and all child entries) to build (NOT NULL) |
@@ -256,17 +552,16 @@ static int __init aafs_create_file(struct aa_fs_entry *fs_file, | |||
256 | static int __init aafs_create_dir(struct aa_fs_entry *fs_dir, | 552 | static int __init aafs_create_dir(struct aa_fs_entry *fs_dir, |
257 | struct dentry *parent) | 553 | struct dentry *parent) |
258 | { | 554 | { |
259 | int error; | ||
260 | struct aa_fs_entry *fs_file; | 555 | struct aa_fs_entry *fs_file; |
556 | struct dentry *dir; | ||
557 | int error; | ||
261 | 558 | ||
262 | fs_dir->dentry = securityfs_create_dir(fs_dir->name, parent); | 559 | dir = securityfs_create_dir(fs_dir->name, parent); |
263 | if (IS_ERR(fs_dir->dentry)) { | 560 | if (IS_ERR(dir)) |
264 | error = PTR_ERR(fs_dir->dentry); | 561 | return PTR_ERR(dir); |
265 | fs_dir->dentry = NULL; | 562 | fs_dir->dentry = dir; |
266 | goto failed; | ||
267 | } | ||
268 | 563 | ||
269 | for (fs_file = fs_dir->v.files; fs_file->name; ++fs_file) { | 564 | for (fs_file = fs_dir->v.files; fs_file && fs_file->name; ++fs_file) { |
270 | if (fs_file->v_type == AA_FS_TYPE_DIR) | 565 | if (fs_file->v_type == AA_FS_TYPE_DIR) |
271 | error = aafs_create_dir(fs_file, fs_dir->dentry); | 566 | error = aafs_create_dir(fs_file, fs_dir->dentry); |
272 | else | 567 | else |
@@ -278,6 +573,8 @@ static int __init aafs_create_dir(struct aa_fs_entry *fs_dir, | |||
278 | return 0; | 573 | return 0; |
279 | 574 | ||
280 | failed: | 575 | failed: |
576 | aafs_remove_dir(fs_dir); | ||
577 | |||
281 | return error; | 578 | return error; |
282 | } | 579 | } |
283 | 580 | ||
@@ -302,7 +599,7 @@ static void __init aafs_remove_dir(struct aa_fs_entry *fs_dir) | |||
302 | { | 599 | { |
303 | struct aa_fs_entry *fs_file; | 600 | struct aa_fs_entry *fs_file; |
304 | 601 | ||
305 | for (fs_file = fs_dir->v.files; fs_file->name; ++fs_file) { | 602 | for (fs_file = fs_dir->v.files; fs_file && fs_file->name; ++fs_file) { |
306 | if (fs_file->v_type == AA_FS_TYPE_DIR) | 603 | if (fs_file->v_type == AA_FS_TYPE_DIR) |
307 | aafs_remove_dir(fs_file); | 604 | aafs_remove_dir(fs_file); |
308 | else | 605 | else |
@@ -346,6 +643,11 @@ static int __init aa_create_aafs(void) | |||
346 | if (error) | 643 | if (error) |
347 | goto error; | 644 | goto error; |
348 | 645 | ||
646 | error = __aa_fs_namespace_mkdir(root_ns, aa_fs_entry.dentry, | ||
647 | "policy"); | ||
648 | if (error) | ||
649 | goto error; | ||
650 | |||
349 | /* TODO: add support for apparmorfs_null and apparmorfs_mnt */ | 651 | /* TODO: add support for apparmorfs_null and apparmorfs_mnt */ |
350 | 652 | ||
351 | /* Report that AppArmor fs is enabled */ | 653 | /* Report that AppArmor fs is enabled */ |