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 | |
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')
-rw-r--r-- | security/apparmor/apparmorfs.c | 322 | ||||
-rw-r--r-- | security/apparmor/include/apparmorfs.h | 38 | ||||
-rw-r--r-- | security/apparmor/include/audit.h | 1 | ||||
-rw-r--r-- | security/apparmor/include/policy.h | 21 | ||||
-rw-r--r-- | security/apparmor/lsm.c | 6 | ||||
-rw-r--r-- | security/apparmor/policy.c | 75 | ||||
-rw-r--r-- | security/apparmor/procattr.c | 2 |
7 files changed, 436 insertions, 29 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 */ |
diff --git a/security/apparmor/include/apparmorfs.h b/security/apparmor/include/apparmorfs.h index 7ea4769fab3f..2494e112f2bf 100644 --- a/security/apparmor/include/apparmorfs.h +++ b/security/apparmor/include/apparmorfs.h | |||
@@ -61,4 +61,42 @@ extern const struct file_operations aa_fs_seq_file_ops; | |||
61 | 61 | ||
62 | extern void __init aa_destroy_aafs(void); | 62 | extern void __init aa_destroy_aafs(void); |
63 | 63 | ||
64 | struct aa_profile; | ||
65 | struct aa_namespace; | ||
66 | |||
67 | enum aafs_ns_type { | ||
68 | AAFS_NS_DIR, | ||
69 | AAFS_NS_PROFS, | ||
70 | AAFS_NS_NS, | ||
71 | AAFS_NS_COUNT, | ||
72 | AAFS_NS_MAX_COUNT, | ||
73 | AAFS_NS_SIZE, | ||
74 | AAFS_NS_MAX_SIZE, | ||
75 | AAFS_NS_OWNER, | ||
76 | AAFS_NS_SIZEOF, | ||
77 | }; | ||
78 | |||
79 | enum aafs_prof_type { | ||
80 | AAFS_PROF_DIR, | ||
81 | AAFS_PROF_PROFS, | ||
82 | AAFS_PROF_NAME, | ||
83 | AAFS_PROF_MODE, | ||
84 | AAFS_PROF_SIZEOF, | ||
85 | }; | ||
86 | |||
87 | #define ns_dir(X) ((X)->dents[AAFS_NS_DIR]) | ||
88 | #define ns_subns_dir(X) ((X)->dents[AAFS_NS_NS]) | ||
89 | #define ns_subprofs_dir(X) ((X)->dents[AAFS_NS_PROFS]) | ||
90 | |||
91 | #define prof_dir(X) ((X)->dents[AAFS_PROF_DIR]) | ||
92 | #define prof_child_dir(X) ((X)->dents[AAFS_PROF_PROFS]) | ||
93 | |||
94 | void __aa_fs_profile_rmdir(struct aa_profile *profile); | ||
95 | void __aa_fs_profile_migrate_dents(struct aa_profile *old, | ||
96 | struct aa_profile *new); | ||
97 | int __aa_fs_profile_mkdir(struct aa_profile *profile, struct dentry *parent); | ||
98 | void __aa_fs_namespace_rmdir(struct aa_namespace *ns); | ||
99 | int __aa_fs_namespace_mkdir(struct aa_namespace *ns, struct dentry *parent, | ||
100 | const char *name); | ||
101 | |||
64 | #endif /* __AA_APPARMORFS_H */ | 102 | #endif /* __AA_APPARMORFS_H */ |
diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h index 69d8cae634e7..30e8d7687259 100644 --- a/security/apparmor/include/audit.h +++ b/security/apparmor/include/audit.h | |||
@@ -27,7 +27,6 @@ struct aa_profile; | |||
27 | 27 | ||
28 | extern const char *const audit_mode_names[]; | 28 | extern const char *const audit_mode_names[]; |
29 | #define AUDIT_MAX_INDEX 5 | 29 | #define AUDIT_MAX_INDEX 5 |
30 | |||
31 | enum audit_mode { | 30 | enum audit_mode { |
32 | AUDIT_NORMAL, /* follow normal auditing of accesses */ | 31 | AUDIT_NORMAL, /* follow normal auditing of accesses */ |
33 | AUDIT_QUIET_DENIED, /* quiet all denied access messages */ | 32 | AUDIT_QUIET_DENIED, /* quiet all denied access messages */ |
diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index 65662e3c75cf..5c72231d1c42 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h | |||
@@ -29,8 +29,8 @@ | |||
29 | #include "file.h" | 29 | #include "file.h" |
30 | #include "resource.h" | 30 | #include "resource.h" |
31 | 31 | ||
32 | extern const char *const profile_mode_names[]; | 32 | extern const char *const aa_profile_mode_names[]; |
33 | #define APPARMOR_NAMES_MAX_INDEX 3 | 33 | #define APPARMOR_MODE_NAMES_MAX_INDEX 4 |
34 | 34 | ||
35 | #define PROFILE_MODE(_profile, _mode) \ | 35 | #define PROFILE_MODE(_profile, _mode) \ |
36 | ((aa_g_profile_mode == (_mode)) || \ | 36 | ((aa_g_profile_mode == (_mode)) || \ |
@@ -110,6 +110,8 @@ struct aa_ns_acct { | |||
110 | * @unconfined: special unconfined profile for the namespace | 110 | * @unconfined: special unconfined profile for the namespace |
111 | * @sub_ns: list of namespaces under the current namespace. | 111 | * @sub_ns: list of namespaces under the current namespace. |
112 | * @uniq_null: uniq value used for null learning profiles | 112 | * @uniq_null: uniq value used for null learning profiles |
113 | * @uniq_id: a unique id count for the profiles in the namespace | ||
114 | * @dents: dentries for the namespaces file entries in apparmorfs | ||
113 | * | 115 | * |
114 | * An aa_namespace defines the set profiles that are searched to determine | 116 | * An aa_namespace defines the set profiles that are searched to determine |
115 | * which profile to attach to a task. Profiles can not be shared between | 117 | * which profile to attach to a task. Profiles can not be shared between |
@@ -133,6 +135,9 @@ struct aa_namespace { | |||
133 | struct aa_profile *unconfined; | 135 | struct aa_profile *unconfined; |
134 | struct list_head sub_ns; | 136 | struct list_head sub_ns; |
135 | atomic_t uniq_null; | 137 | atomic_t uniq_null; |
138 | long uniq_id; | ||
139 | |||
140 | struct dentry *dents[AAFS_NS_SIZEOF]; | ||
136 | }; | 141 | }; |
137 | 142 | ||
138 | /* struct aa_policydb - match engine for a policy | 143 | /* struct aa_policydb - match engine for a policy |
@@ -172,6 +177,9 @@ struct aa_replacedby { | |||
172 | * @caps: capabilities for the profile | 177 | * @caps: capabilities for the profile |
173 | * @rlimits: rlimits for the profile | 178 | * @rlimits: rlimits for the profile |
174 | * | 179 | * |
180 | * @dents: dentries for the profiles file entries in apparmorfs | ||
181 | * @dirname: name of the profile dir in apparmorfs | ||
182 | * | ||
175 | * The AppArmor profile contains the basic confinement data. Each profile | 183 | * The AppArmor profile contains the basic confinement data. Each profile |
176 | * has a name, and exists in a namespace. The @name and @exec_match are | 184 | * has a name, and exists in a namespace. The @name and @exec_match are |
177 | * used to determine profile attachment against unconfined tasks. All other | 185 | * used to determine profile attachment against unconfined tasks. All other |
@@ -208,6 +216,9 @@ struct aa_profile { | |||
208 | struct aa_file_rules file; | 216 | struct aa_file_rules file; |
209 | struct aa_caps caps; | 217 | struct aa_caps caps; |
210 | struct aa_rlimit rlimits; | 218 | struct aa_rlimit rlimits; |
219 | |||
220 | char *dirname; | ||
221 | struct dentry *dents[AAFS_PROF_SIZEOF]; | ||
211 | }; | 222 | }; |
212 | 223 | ||
213 | extern struct aa_namespace *root_ns; | 224 | extern struct aa_namespace *root_ns; |
@@ -243,6 +254,12 @@ ssize_t aa_remove_profiles(char *name, size_t size); | |||
243 | #define unconfined(X) ((X)->mode == APPARMOR_UNCONFINED) | 254 | #define unconfined(X) ((X)->mode == APPARMOR_UNCONFINED) |
244 | 255 | ||
245 | 256 | ||
257 | static inline struct aa_profile *aa_deref_parent(struct aa_profile *p) | ||
258 | { | ||
259 | return rcu_dereference_protected(p->parent, | ||
260 | mutex_is_locked(&p->ns->lock)); | ||
261 | } | ||
262 | |||
246 | /** | 263 | /** |
247 | * aa_get_profile - increment refcount on profile @p | 264 | * aa_get_profile - increment refcount on profile @p |
248 | * @p: profile (MAYBE NULL) | 265 | * @p: profile (MAYBE NULL) |
diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index c8c148a738f7..edb3ce15e92d 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c | |||
@@ -843,7 +843,7 @@ static int param_get_mode(char *buffer, struct kernel_param *kp) | |||
843 | if (!apparmor_enabled) | 843 | if (!apparmor_enabled) |
844 | return -EINVAL; | 844 | return -EINVAL; |
845 | 845 | ||
846 | return sprintf(buffer, "%s", profile_mode_names[aa_g_profile_mode]); | 846 | return sprintf(buffer, "%s", aa_profile_mode_names[aa_g_profile_mode]); |
847 | } | 847 | } |
848 | 848 | ||
849 | static int param_set_mode(const char *val, struct kernel_param *kp) | 849 | static int param_set_mode(const char *val, struct kernel_param *kp) |
@@ -858,8 +858,8 @@ static int param_set_mode(const char *val, struct kernel_param *kp) | |||
858 | if (!val) | 858 | if (!val) |
859 | return -EINVAL; | 859 | return -EINVAL; |
860 | 860 | ||
861 | for (i = 0; i < APPARMOR_NAMES_MAX_INDEX; i++) { | 861 | for (i = 0; i < APPARMOR_MODE_NAMES_MAX_INDEX; i++) { |
862 | if (strcmp(val, profile_mode_names[i]) == 0) { | 862 | if (strcmp(val, aa_profile_mode_names[i]) == 0) { |
863 | aa_g_profile_mode = i; | 863 | aa_g_profile_mode = i; |
864 | return 0; | 864 | return 0; |
865 | } | 865 | } |
diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 2e4e2ecb25bc..6172509fa2b7 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c | |||
@@ -92,7 +92,7 @@ | |||
92 | /* root profile namespace */ | 92 | /* root profile namespace */ |
93 | struct aa_namespace *root_ns; | 93 | struct aa_namespace *root_ns; |
94 | 94 | ||
95 | const char *const profile_mode_names[] = { | 95 | const char *const aa_profile_mode_names[] = { |
96 | "enforce", | 96 | "enforce", |
97 | "complain", | 97 | "complain", |
98 | "kill", | 98 | "kill", |
@@ -394,7 +394,13 @@ static struct aa_namespace *aa_prepare_namespace(const char *name) | |||
394 | ns = alloc_namespace(root->base.hname, name); | 394 | ns = alloc_namespace(root->base.hname, name); |
395 | if (!ns) | 395 | if (!ns) |
396 | goto out; | 396 | goto out; |
397 | /* add parent ref */ | 397 | if (__aa_fs_namespace_mkdir(ns, ns_subns_dir(root), name)) { |
398 | AA_ERROR("Failed to create interface for ns %s\n", | ||
399 | ns->base.name); | ||
400 | free_namespace(ns); | ||
401 | ns = NULL; | ||
402 | goto out; | ||
403 | } | ||
398 | ns->parent = aa_get_namespace(root); | 404 | ns->parent = aa_get_namespace(root); |
399 | list_add_rcu(&ns->base.list, &root->sub_ns); | 405 | list_add_rcu(&ns->base.list, &root->sub_ns); |
400 | /* add list ref */ | 406 | /* add list ref */ |
@@ -456,6 +462,7 @@ static void __remove_profile(struct aa_profile *profile) | |||
456 | __profile_list_release(&profile->base.profiles); | 462 | __profile_list_release(&profile->base.profiles); |
457 | /* released by free_profile */ | 463 | /* released by free_profile */ |
458 | __aa_update_replacedby(profile, profile->ns->unconfined); | 464 | __aa_update_replacedby(profile, profile->ns->unconfined); |
465 | __aa_fs_profile_rmdir(profile); | ||
459 | __list_remove_profile(profile); | 466 | __list_remove_profile(profile); |
460 | } | 467 | } |
461 | 468 | ||
@@ -492,6 +499,7 @@ static void destroy_namespace(struct aa_namespace *ns) | |||
492 | 499 | ||
493 | if (ns->parent) | 500 | if (ns->parent) |
494 | __aa_update_replacedby(ns->unconfined, ns->parent->unconfined); | 501 | __aa_update_replacedby(ns->unconfined, ns->parent->unconfined); |
502 | __aa_fs_namespace_rmdir(ns); | ||
495 | mutex_unlock(&ns->lock); | 503 | mutex_unlock(&ns->lock); |
496 | } | 504 | } |
497 | 505 | ||
@@ -596,6 +604,7 @@ void aa_free_profile(struct aa_profile *profile) | |||
596 | aa_free_cap_rules(&profile->caps); | 604 | aa_free_cap_rules(&profile->caps); |
597 | aa_free_rlimit_rules(&profile->rlimits); | 605 | aa_free_rlimit_rules(&profile->rlimits); |
598 | 606 | ||
607 | kzfree(profile->dirname); | ||
599 | aa_put_dfa(profile->xmatch); | 608 | aa_put_dfa(profile->xmatch); |
600 | aa_put_dfa(profile->policy.dfa); | 609 | aa_put_dfa(profile->policy.dfa); |
601 | aa_put_replacedby(profile->replacedby); | 610 | aa_put_replacedby(profile->replacedby); |
@@ -986,8 +995,7 @@ static void __replace_profile(struct aa_profile *old, struct aa_profile *new, | |||
986 | /* inherit @child and its children */ | 995 | /* inherit @child and its children */ |
987 | /* TODO: update hname of inherited children */ | 996 | /* TODO: update hname of inherited children */ |
988 | /* list refcount transferred to @new */ | 997 | /* list refcount transferred to @new */ |
989 | p = rcu_dereference_protected(child->parent, | 998 | p = aa_deref_parent(child); |
990 | mutex_is_locked(&child->ns->lock)); | ||
991 | rcu_assign_pointer(child->parent, aa_get_profile(new)); | 999 | rcu_assign_pointer(child->parent, aa_get_profile(new)); |
992 | list_add_rcu(&child->base.list, &new->base.profiles); | 1000 | list_add_rcu(&child->base.list, &new->base.profiles); |
993 | aa_put_profile(p); | 1001 | aa_put_profile(p); |
@@ -995,14 +1003,18 @@ static void __replace_profile(struct aa_profile *old, struct aa_profile *new, | |||
995 | } | 1003 | } |
996 | 1004 | ||
997 | if (!rcu_access_pointer(new->parent)) { | 1005 | if (!rcu_access_pointer(new->parent)) { |
998 | struct aa_profile *parent = rcu_dereference(old->parent); | 1006 | struct aa_profile *parent = aa_deref_parent(old); |
999 | rcu_assign_pointer(new->parent, aa_get_profile(parent)); | 1007 | rcu_assign_pointer(new->parent, aa_get_profile(parent)); |
1000 | } | 1008 | } |
1001 | __aa_update_replacedby(old, new); | 1009 | __aa_update_replacedby(old, new); |
1002 | if (share_replacedby) { | 1010 | if (share_replacedby) { |
1003 | aa_put_replacedby(new->replacedby); | 1011 | aa_put_replacedby(new->replacedby); |
1004 | new->replacedby = aa_get_replacedby(old->replacedby); | 1012 | new->replacedby = aa_get_replacedby(old->replacedby); |
1005 | } | 1013 | } else if (!rcu_access_pointer(new->replacedby->profile)) |
1014 | /* aafs interface uses replacedby */ | ||
1015 | rcu_assign_pointer(new->replacedby->profile, | ||
1016 | aa_get_profile(new)); | ||
1017 | __aa_fs_profile_migrate_dents(old, new); | ||
1006 | 1018 | ||
1007 | if (list_empty(&new->base.list)) { | 1019 | if (list_empty(&new->base.list)) { |
1008 | /* new is not on a list already */ | 1020 | /* new is not on a list already */ |
@@ -1118,7 +1130,33 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) | |||
1118 | } | 1130 | } |
1119 | } | 1131 | } |
1120 | 1132 | ||
1121 | /* do actual replacement */ | 1133 | /* create new fs entries for introspection if needed */ |
1134 | list_for_each_entry(ent, &lh, list) { | ||
1135 | if (ent->old) { | ||
1136 | /* inherit old interface files */ | ||
1137 | |||
1138 | /* if (ent->rename) | ||
1139 | TODO: support rename */ | ||
1140 | /* } else if (ent->rename) { | ||
1141 | TODO: support rename */ | ||
1142 | } else { | ||
1143 | struct dentry *parent; | ||
1144 | if (rcu_access_pointer(ent->new->parent)) { | ||
1145 | struct aa_profile *p; | ||
1146 | p = aa_deref_parent(ent->new); | ||
1147 | parent = prof_child_dir(p); | ||
1148 | } else | ||
1149 | parent = ns_subprofs_dir(ent->new->ns); | ||
1150 | error = __aa_fs_profile_mkdir(ent->new, parent); | ||
1151 | } | ||
1152 | |||
1153 | if (error) { | ||
1154 | info = "failed to create "; | ||
1155 | goto fail_lock; | ||
1156 | } | ||
1157 | } | ||
1158 | |||
1159 | /* Done with checks that may fail - do actual replacement */ | ||
1122 | list_for_each_entry_safe(ent, tmp, &lh, list) { | 1160 | list_for_each_entry_safe(ent, tmp, &lh, list) { |
1123 | list_del_init(&ent->list); | 1161 | list_del_init(&ent->list); |
1124 | op = (!ent->old && !ent->rename) ? OP_PROF_LOAD : OP_PROF_REPL; | 1162 | op = (!ent->old && !ent->rename) ? OP_PROF_LOAD : OP_PROF_REPL; |
@@ -1127,14 +1165,21 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) | |||
1127 | 1165 | ||
1128 | if (ent->old) { | 1166 | if (ent->old) { |
1129 | __replace_profile(ent->old, ent->new, 1); | 1167 | __replace_profile(ent->old, ent->new, 1); |
1130 | if (ent->rename) | 1168 | if (ent->rename) { |
1169 | /* aafs interface uses replacedby */ | ||
1170 | struct aa_replacedby *r = ent->new->replacedby; | ||
1171 | rcu_assign_pointer(r->profile, | ||
1172 | aa_get_profile(ent->new)); | ||
1131 | __replace_profile(ent->rename, ent->new, 0); | 1173 | __replace_profile(ent->rename, ent->new, 0); |
1174 | } | ||
1132 | } else if (ent->rename) { | 1175 | } else if (ent->rename) { |
1176 | /* aafs interface uses replacedby */ | ||
1177 | rcu_assign_pointer(ent->new->replacedby->profile, | ||
1178 | aa_get_profile(ent->new)); | ||
1133 | __replace_profile(ent->rename, ent->new, 0); | 1179 | __replace_profile(ent->rename, ent->new, 0); |
1134 | } else if (ent->new->parent) { | 1180 | } else if (ent->new->parent) { |
1135 | struct aa_profile *parent, *newest; | 1181 | struct aa_profile *parent, *newest; |
1136 | parent = rcu_dereference_protected(ent->new->parent, | 1182 | parent = aa_deref_parent(ent->new); |
1137 | mutex_is_locked(&ns->lock)); | ||
1138 | newest = aa_get_newest_profile(parent); | 1183 | newest = aa_get_newest_profile(parent); |
1139 | 1184 | ||
1140 | /* parent replaced in this atomic set? */ | 1185 | /* parent replaced in this atomic set? */ |
@@ -1144,10 +1189,16 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) | |||
1144 | rcu_assign_pointer(ent->new->parent, newest); | 1189 | rcu_assign_pointer(ent->new->parent, newest); |
1145 | } else | 1190 | } else |
1146 | aa_put_profile(newest); | 1191 | aa_put_profile(newest); |
1192 | /* aafs interface uses replacedby */ | ||
1193 | rcu_assign_pointer(ent->new->replacedby->profile, | ||
1194 | aa_get_profile(ent->new)); | ||
1147 | __list_add_profile(&parent->base.profiles, ent->new); | 1195 | __list_add_profile(&parent->base.profiles, ent->new); |
1148 | } else | 1196 | } else { |
1197 | /* aafs interface uses replacedby */ | ||
1198 | rcu_assign_pointer(ent->new->replacedby->profile, | ||
1199 | aa_get_profile(ent->new)); | ||
1149 | __list_add_profile(&ns->base.profiles, ent->new); | 1200 | __list_add_profile(&ns->base.profiles, ent->new); |
1150 | 1201 | } | |
1151 | aa_load_ent_free(ent); | 1202 | aa_load_ent_free(ent); |
1152 | } | 1203 | } |
1153 | mutex_unlock(&ns->lock); | 1204 | mutex_unlock(&ns->lock); |
diff --git a/security/apparmor/procattr.c b/security/apparmor/procattr.c index 6c9390179b89..b125acc9aa26 100644 --- a/security/apparmor/procattr.c +++ b/security/apparmor/procattr.c | |||
@@ -37,7 +37,7 @@ int aa_getprocattr(struct aa_profile *profile, char **string) | |||
37 | { | 37 | { |
38 | char *str; | 38 | char *str; |
39 | int len = 0, mode_len = 0, ns_len = 0, name_len; | 39 | int len = 0, mode_len = 0, ns_len = 0, name_len; |
40 | const char *mode_str = profile_mode_names[profile->mode]; | 40 | const char *mode_str = aa_profile_mode_names[profile->mode]; |
41 | const char *ns_name = NULL; | 41 | const char *ns_name = NULL; |
42 | struct aa_namespace *ns = profile->ns; | 42 | struct aa_namespace *ns = profile->ns; |
43 | struct aa_namespace *current_ns = __aa_current_profile()->ns; | 43 | struct aa_namespace *current_ns = __aa_current_profile()->ns; |